Poll and sticker improvements

This commit is contained in:
Ali 2020-04-16 01:44:57 +04:00
parent 203acd8567
commit c8d87f8998
23 changed files with 549 additions and 233 deletions

View File

@ -530,7 +530,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
if isPlaying {
self.play()
} else{
self.stop()
self.pause()
}
}
let canDisplayFirstFrame = self.automaticallyLoadFirstFrame && self.isDisplaying
@ -542,79 +542,161 @@ public final class AnimatedStickerNode: ASDisplayNode {
}
}
private var isSetUpForPlayback = false
public func play(firstFrame: Bool = false) {
let directData = self.directData
let cachedData = self.cachedData
let queue = self.queue
let timerHolder = self.timer
let frameSourceHolder = self.frameSource
self.queue.async { [weak self] in
var maybeFrameSource: AnimatedStickerFrameSource?
let notifyUpdated: (() -> Void)? = nil
if let directData = directData {
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3)
} else if let (cachedData, cachedDataComplete) = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {
notifyUpdated?()
if self.isSetUpForPlayback {
let directData = self.directData
let cachedData = self.cachedData
let queue = self.queue
let timerHolder = self.timer
let frameSourceHolder = self.frameSource
self.queue.async { [weak self] in
var maybeFrameSource: AnimatedStickerFrameSource? = frameSourceHolder.with { $0 }?.syncWith { $0 }?.value
if maybeFrameSource == nil {
let notifyUpdated: (() -> Void)? = nil
if let directData = directData {
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3)
} else if let (cachedData, cachedDataComplete) = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {
notifyUpdated?()
})
}
}
let _ = frameSourceHolder.swap(maybeFrameSource.flatMap { maybeFrameSource in
return QueueLocalObject(queue: queue, generate: {
return AnimatedStickerFrameSourceWrapper(maybeFrameSource)
})
})
}
}
let _ = frameSourceHolder.swap(maybeFrameSource.flatMap { maybeFrameSource in
return QueueLocalObject(queue: queue, generate: {
return AnimatedStickerFrameSourceWrapper(maybeFrameSource)
})
})
guard let frameSource = maybeFrameSource else {
return
}
let frameQueue = QueueLocalObject<AnimatedStickerFrameQueue>(queue: queue, generate: {
return AnimatedStickerFrameQueue(queue: queue, length: 1, source: frameSource)
})
timerHolder.swap(nil)?.invalidate()
let duration: Double = frameSource.frameRate > 0 ? Double(frameSource.frameCount) / Double(frameSource.frameRate) : 0
let frameRate = frameSource.frameRate
let timer = SwiftSignalKit.Timer(timeout: 1.0 / Double(frameRate), repeat: !firstFrame, completion: {
let maybeFrame = frameQueue.syncWith { frameQueue in
return frameQueue.take()
guard let frameSource = maybeFrameSource else {
return
}
if let maybeFrame = maybeFrame, let frame = maybeFrame {
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, completion: {
let frameQueue = QueueLocalObject<AnimatedStickerFrameQueue>(queue: queue, generate: {
return AnimatedStickerFrameQueue(queue: queue, length: 1, source: frameSource)
})
timerHolder.swap(nil)?.invalidate()
let duration: Double = frameSource.frameRate > 0 ? Double(frameSource.frameCount) / Double(frameSource.frameRate) : 0
let frameRate = frameSource.frameRate
let timer = SwiftSignalKit.Timer(timeout: 1.0 / Double(frameRate), repeat: !firstFrame, completion: {
let maybeFrame = frameQueue.syncWith { frameQueue in
return frameQueue.take()
}
if let maybeFrame = maybeFrame, let frame = maybeFrame {
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
if !strongSelf.reportedStarted {
strongSelf.reportedStarted = true
strongSelf.started()
strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, completion: {
guard let strongSelf = self else {
return
}
if !strongSelf.reportedStarted {
strongSelf.reportedStarted = true
strongSelf.started()
}
})
if case .once = strongSelf.playbackMode, frame.isLastFrame {
strongSelf.stop()
strongSelf.isPlaying = false
}
})
if case .once = strongSelf.playbackMode, frame.isLastFrame {
strongSelf.stop()
strongSelf.isPlaying = false
let timestamp: Double = frameRate > 0 ? Double(frame.index) / Double(frameRate) : 0
strongSelf.playbackStatus.set(.single(AnimatedStickerStatus(playing: strongSelf.isPlaying, duration: duration, timestamp: timestamp)))
}
let timestamp: Double = frameRate > 0 ? Double(frame.index) / Double(frameRate) : 0
strongSelf.playbackStatus.set(.single(AnimatedStickerStatus(playing: strongSelf.isPlaying, duration: duration, timestamp: timestamp)))
}
frameQueue.with { frameQueue in
frameQueue.generateFramesIfNeeded()
}
}, queue: queue)
let _ = timerHolder.swap(timer)
timer.start()
}
} else {
self.isSetUpForPlayback = true
let directData = self.directData
let cachedData = self.cachedData
let queue = self.queue
let timerHolder = self.timer
let frameSourceHolder = self.frameSource
self.queue.async { [weak self] in
var maybeFrameSource: AnimatedStickerFrameSource?
let notifyUpdated: (() -> Void)? = nil
if let directData = directData {
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3)
} else if let (cachedData, cachedDataComplete) = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {
notifyUpdated?()
})
}
}
frameQueue.with { frameQueue in
frameQueue.generateFramesIfNeeded()
let _ = frameSourceHolder.swap(maybeFrameSource.flatMap { maybeFrameSource in
return QueueLocalObject(queue: queue, generate: {
return AnimatedStickerFrameSourceWrapper(maybeFrameSource)
})
})
guard let frameSource = maybeFrameSource else {
return
}
}, queue: queue)
let _ = timerHolder.swap(timer)
timer.start()
let frameQueue = QueueLocalObject<AnimatedStickerFrameQueue>(queue: queue, generate: {
return AnimatedStickerFrameQueue(queue: queue, length: 1, source: frameSource)
})
timerHolder.swap(nil)?.invalidate()
let duration: Double = frameSource.frameRate > 0 ? Double(frameSource.frameCount) / Double(frameSource.frameRate) : 0
let frameRate = frameSource.frameRate
let timer = SwiftSignalKit.Timer(timeout: 1.0 / Double(frameRate), repeat: !firstFrame, completion: {
let maybeFrame = frameQueue.syncWith { frameQueue in
return frameQueue.take()
}
if let maybeFrame = maybeFrame, let frame = maybeFrame {
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, completion: {
guard let strongSelf = self else {
return
}
if !strongSelf.reportedStarted {
strongSelf.reportedStarted = true
strongSelf.started()
}
})
if case .once = strongSelf.playbackMode, frame.isLastFrame {
strongSelf.stop()
strongSelf.isPlaying = false
}
let timestamp: Double = frameRate > 0 ? Double(frame.index) / Double(frameRate) : 0
strongSelf.playbackStatus.set(.single(AnimatedStickerStatus(playing: strongSelf.isPlaying, duration: duration, timestamp: timestamp)))
}
}
frameQueue.with { frameQueue in
frameQueue.generateFramesIfNeeded()
}
}, queue: queue)
let _ = timerHolder.swap(timer)
timer.start()
}
}
}
public func pause() {
self.timer.swap(nil)?.invalidate()
}
public func stop() {
self.isSetUpForPlayback = false
self.reportedStarted = false
self.timer.swap(nil)?.invalidate()
if self.playToCompletionOnStop {

View File

@ -1116,6 +1116,7 @@ public func createPollController(context: AccountContext, peer: Peer, isQuiz: Bo
}
controller.isOpaqueWhenInOverlay = true
controller.blocksBackgroundWhenInOverlay = true
controller.acceptsFocusWhenInOverlay = true
controller.experimentalSnapScrollToItem = true
controller.alwaysSynchronous = true

View File

@ -96,6 +96,17 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
}
}
var isInFocus: Bool = false {
didSet {
if self.isInFocus != oldValue {
self.inFocusUpdated(isInFocus: self.isInFocus)
}
}
}
func inFocusUpdated(isInFocus: Bool) {
self.state.top?.value.isInFocus = isInFocus
}
private var currentKeyboardLeftEdge: CGFloat = 0.0
private var additionalKeyboardLeftEdgeOffset: CGFloat = 0.0
@ -322,12 +333,14 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
if pending.isReady {
self.state.pending = nil
let previous = self.state.top
previous?.value.isInFocus = false
self.state.top = pending.value
var updatedLayout = layout
if pending.value.value.view.disableAutomaticKeyboardHandling.isEmpty {
updatedLayout = updatedLayout.withUpdatedInputHeight(nil)
}
self.topTransition(from: previous, to: pending.value, transitionType: pending.transitionType, layout: updatedLayout, transition: pending.transition)
self.state.top?.value.isInFocus = self.isInFocus
statusBarTransition = pending.transition
if !self.isReady {
self.isReady = true
@ -338,6 +351,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
if controllers.isEmpty && self.state.top != nil {
let previous = self.state.top
previous?.value.isInFocus = false
self.state.top = nil
self.topTransition(from: previous, to: nil, transitionType: .pop, layout: layout, transition: .immediate)
}

View File

@ -933,6 +933,44 @@ open class NavigationController: UINavigationController, ContainableController,
self.statusBarHost?.setStatusBarHidden(statusBarHidden, animated: animateStatusBarStyleTransition)
}
var foundControllerInFocus = false
for container in self.overlayContainers.reversed() {
if foundControllerInFocus {
container.controller.isInFocus = false
} else if container.controller.acceptsFocusWhenInOverlay {
foundControllerInFocus = true
container.controller.isInFocus = true
}
}
for container in self.modalContainers.reversed() {
if foundControllerInFocus {
container.container.isInFocus = false
} else {
foundControllerInFocus = true
container.container.isInFocus = true
}
}
if let rootContainer = self.rootContainer {
switch rootContainer {
case let .flat(container):
if foundControllerInFocus {
container.isInFocus = false
} else {
foundControllerInFocus = true
container.isInFocus = true
}
case let .split(split):
if foundControllerInFocus {
split.isInFocus = false
} else {
foundControllerInFocus = true
split.isInFocus = true
}
}
}
self.isUpdatingContainers = false
if notifyGlobalOverlayControllersUpdated {

View File

@ -27,6 +27,18 @@ final class NavigationSplitContainer: ASDisplayNode {
}
}
var isInFocus: Bool = false {
didSet {
if self.isInFocus != oldValue {
self.inFocusUpdated(isInFocus: self.isInFocus)
}
}
}
func inFocusUpdated(isInFocus: Bool) {
self.masterContainer.isInFocus = isInFocus
self.detailContainer.isInFocus = isInFocus
}
init(theme: NavigationControllerTheme, controllerRemoved: @escaping (ViewController) -> Void, scrollToTop: @escaping (NavigationSplitContainerScrollToTop) -> Void) {
self.theme = theme

View File

@ -103,9 +103,14 @@ public enum TabBarItemContextActionType {
public final var isOpaqueWhenInOverlay: Bool = false
public final var blocksBackgroundWhenInOverlay: Bool = false
public final var acceptsFocusWhenInOverlay: Bool = false
public final var automaticallyControlPresentationContextLayout: Bool = true
public var updateTransitionWhenPresentedAsModal: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
public func requestUpdateParameters() {
self.modalStyleOverlayTransitionFactorUpdated?(.immediate)
}
public func combinedSupportedOrientations(currentOrientationToLock: UIInterfaceOrientationMask) -> ViewControllerSupportedOrientations {
return self.supportedOrientations
}
@ -272,6 +277,16 @@ public enum TabBarItemContextActionType {
return nil
}
}
public internal(set) var isInFocus: Bool = false {
didSet {
if self.isInFocus != oldValue {
self.inFocusUpdated(isInFocus: self.isInFocus)
}
}
}
open func inFocusUpdated(isInFocus: Bool) {
}
public var attemptNavigation: (@escaping () -> Void) -> Bool = { _ in
return true

View File

@ -800,6 +800,7 @@ public class GalleryController: ViewController, StandalonePresentableController
}
self.blocksBackgroundWhenInOverlay = true
self.acceptsFocusWhenInOverlay = true
self.isOpaqueWhenInOverlay = true
}

View File

@ -326,6 +326,7 @@ final class BubbleSettingsController: ViewController {
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings))
self.blocksBackgroundWhenInOverlay = true
self.acceptsFocusWhenInOverlay = true
self.navigationPresentation = .modal
self.navigationItem.title = self.presentationData.strings.Appearance_BubbleCorners_Title

View File

@ -498,6 +498,7 @@ final class TextSizeSelectionController: ViewController {
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings))
self.blocksBackgroundWhenInOverlay = true
self.acceptsFocusWhenInOverlay = true
self.navigationPresentation = .modal
self.navigationItem.title = self.presentationData.strings.Appearance_TextSize_Title

View File

@ -63,6 +63,7 @@ public final class ThemePreviewController: ViewController {
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.previewTheme, presentationStrings: self.presentationData.strings))
self.blocksBackgroundWhenInOverlay = true
self.acceptsFocusWhenInOverlay = true
self.navigationPresentation = .modal
var hasInstallsCount = false

View File

@ -75,6 +75,7 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese
super.init(navigationBarPresentationData: nil)
self.acceptsFocusWhenInOverlay = true
self.statusBar.statusBarStyle = .Ignore
self.stickerPackContents.set(loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: stickerPack, forceActualized: true))
@ -257,6 +258,8 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese
} else {
return
}
self.acceptsFocusWhenInOverlay = false
self.requestUpdateParameters()
self.controllerNode.animateOut(completion: completion)
}

View File

@ -375,6 +375,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.automaticallyControlPresentationContextLayout = false
self.blocksBackgroundWhenInOverlay = true
self.acceptsFocusWhenInOverlay = true
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
@ -1699,7 +1700,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
for contentNode in itemNode.contentNodes {
if let contentNode = contentNode as? ChatMessagePollBubbleContentNode {
let sourceNode = contentNode.solutionTipSourceNode
strongSelf.controllerInteraction?.displayPollSolution(solution, sourceNode)
strongSelf.displayPollSolution(solution: solution, sourceNode: sourceNode, isAutomatic: true)
}
}
}
@ -1948,149 +1949,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
strongSelf.presentPollCreation(isQuiz: isQuiz)
}, displayPollSolution: { [weak self] solution, sourceNode in
guard let strongSelf = self else {
return
}
/*var foundItemNode: ListViewItemNode?
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
if sourceNode.view.isDescendant(of: itemNode.view) {
foundItemNode = itemNode
}
}
}
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))
}, 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.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)
self?.displayPollSolution(solution: solution, sourceNode: sourceNode, isAutomatic: false)
}, requestMessageUpdate: { [weak self] id in
if let strongSelf = self {
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
@ -5010,6 +4869,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
controller.dismissWithCommitAction()
}
})
self.forEachController({ controller in
if let controller = controller as? TooltipScreen {
controller.dismiss()
}
return true
})
self.sendMessageActionsController?.dismiss()
}
@ -5050,6 +4915,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
override public func inFocusUpdated(isInFocus: Bool) {
self.chatDisplayNode.inFocusUpdated(isInFocus: isInFocus)
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
@ -5425,6 +5294,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
private func updatePollTooltipMessageState(animated: Bool) {
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageBubbleItemNode {
for contentNode in itemNode.contentNodes {
if let contentNode = contentNode as? ChatMessagePollBubbleContentNode {
contentNode.updatePollTooltipMessageState(animated: animated)
}
}
}
}
}
private func updateItemNodesSearchTextHighlightStates() {
var searchString: String?
var resultsMessageIndices: [MessageIndex]?
@ -5953,6 +5834,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let legacyController = LegacyController(presentation: .custom, theme: strongSelf.presentationData.theme, initialLayout: strongSelf.validLayout)
legacyController.blocksBackgroundWhenInOverlay = true
legacyController.acceptsFocusWhenInOverlay = true
legacyController.statusBar.statusBarStyle = .Ignore
legacyController.controllerLoaded = { [weak legacyController] in
legacyController?.view.disablesInteractiveTransitionGestureRecognizer = true
@ -6438,6 +6320,164 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}))
}
private func displayPollSolution(solution: TelegramMediaPollResults.Solution, sourceNode: ASDisplayNode, isAutomatic: Bool) {
var maybeFoundItemNode: ChatMessageItemView?
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
if sourceNode.view.isDescendant(of: itemNode.view) {
maybeFoundItemNode = itemNode
}
}
}
guard let foundItemNode = maybeFoundItemNode, let item = foundItemNode.item else {
return
}
/*
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))
}, 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.present(tooltipScreen, in: .current)
}*/
var found = false
self.forEachController({ controller in
if let controller = controller as? TooltipScreen {
if controller.text == solution.text && controller.textEntities == solution.entities {
found = true
controller.dismiss()
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: { [weak self] 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)
}
}
})
let messageId = item.message.id
self.controllerInteraction?.currentPollMessageWithTooltip = messageId
self.updatePollTooltipMessageState(animated: !isAutomatic)
tooltipScreen.willBecomeDismissed = { [weak self] tooltipScreen in
guard let strongSelf = self else {
return
}
//strongSelf.currentMessageTooltipScreens.removeAll(where: { $0.0 === tooltipScreen })
if strongSelf.controllerInteraction?.currentPollMessageWithTooltip == messageId {
strongSelf.controllerInteraction?.currentPollMessageWithTooltip = nil
strongSelf.updatePollTooltipMessageState(animated: true)
}
}
//strongSelf.currentMessageTooltipScreens.append((tooltipScreen, foundItemNode))
self.forEachController({ controller in
if let controller = controller as? TooltipScreen {
controller.dismiss()
}
return true
})
self.present(tooltipScreen, in: .current)
}
private func presentPollCreation(isQuiz: Bool? = nil) {
if case .peer = self.chatLocation, let peer = self.presentationInterfaceState.renderedPeer?.peer {
self.effectiveNavigationController?.pushViewController(createPollController(context: self.context, peer: peer, isQuiz: isQuiz, completion: { [weak self] message in

View File

@ -112,12 +112,14 @@ public final class ChatControllerInteraction {
let requestMessageUpdate: (MessageId) -> Void
let cancelInteractiveKeyboardGestures: () -> Void
var canPlayMedia: Bool = false
var hiddenMedia: [MessageId: [Media]] = [:]
var selectionState: ChatInterfaceSelectionState?
var highlightedState: ChatInterfaceHighlightedState?
var contextHighlightedState: ChatInterfaceHighlightedState?
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
var pollActionState: ChatInterfacePollActionState
var currentPollMessageWithTooltip: MessageId?
var stickerSettings: ChatInterfaceStickerSettings
var searchTextHighightState: (String, [MessageIndex])?
var seenOneTimeAnimatedMedia = Set<MessageId>()

View File

@ -458,6 +458,14 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
private var isInFocus: Bool = false
func inFocusUpdated(isInFocus: Bool) {
self.isInFocus = isInFocus
self.inputMediaNode?.simulateUpdateLayout(isVisible: isInFocus)
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction:
(ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void) {
let transition: ContainedViewLayoutTransition
@ -647,7 +655,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.insertSubnode(inputNode, aboveSubnode: self.inputPanelBackgroundNode)
}
}
inputNodeHeightAndOverflow = inputNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: layout.standardInputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelNodeBaseHeight, transition: immediatelyLayoutInputNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: true)
inputNodeHeightAndOverflow = inputNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: layout.standardInputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelNodeBaseHeight, transition: immediatelyLayoutInputNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: self.isInFocus)
} else if let inputNode = self.inputNode {
dismissedInputNode = inputNode
self.inputNode = nil

View File

@ -1346,6 +1346,12 @@ final class ChatMediaInputNode: ChatInputNode {
}
}
func simulateUpdateLayout(isVisible: Bool) {
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, _) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible)
}
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool) -> (CGFloat, CGFloat) {
var searchMode: ChatMediaInputSearchMode?
if let (_, _, _, _, _, _, _, _, interfaceState, _, _) = self.validLayout, case let .media(_, maybeExpanded) = interfaceState.inputMode, let expanded = maybeExpanded, case let .search(mode) = expanded {

View File

@ -87,13 +87,15 @@ final class TrendingPanePackEntry: Identifiable, Comparable {
func item(account: Account, interaction: TrendingPaneInteraction, grid: Bool) -> GridItem {
let info = self.info
let itemContext = StickerPaneSearchGlobalItemContext()
itemContext.canPlayMedia = true
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)
}, getItemIsPreviewed: { item in
return interaction.getItemIsPreviewed(item)
})
}, itemContext: itemContext)
}
}

View File

@ -802,7 +802,7 @@ private let labelsFont = Font.regular(14.0)
private final class SolutionButtonNode: HighlightableButtonNode {
private let pressed: () -> Void
private let iconNode: ASImageNode
let iconNode: ASImageNode
private var theme: PresentationTheme?
private var incoming: Bool?
@ -1542,6 +1542,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: CGSize(width: resultSize.width, height: 44.0))
strongSelf.updateSelection()
strongSelf.updatePollTooltipMessageState(animated: false)
}
})
})
@ -1740,6 +1741,23 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
}
return nil
}
func updatePollTooltipMessageState(animated: Bool) {
guard let item = self.item else {
return
}
let displaySolutionButton = item.message.id != item.controllerInteraction.currentPollMessageWithTooltip
if displaySolutionButton != !self.solutionButtonNode.iconNode.alpha.isZero {
let transition: ContainedViewLayoutTransition
if animated {
transition = .animated(duration: 0.25, curve: .easeInOut)
} else {
transition = .immediate
}
transition.updateAlpha(node: self.solutionButtonNode.iconNode, alpha: displaySolutionButton ? 1.0 : 0.0)
transition.updateSublayerTransformScale(node: self.solutionButtonNode, scale: displaySolutionButton ? 1.0 : 0.1)
}
}
}
private enum PeerAvatarReference: Equatable {

View File

@ -81,6 +81,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
self.blocksBackgroundWhenInOverlay = true
self.acceptsFocusWhenInOverlay = true
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style

View File

@ -21,6 +21,7 @@ private final class FeaturedInteraction {
let openPack: (ItemCollectionInfo) -> Void
let getItemIsPreviewed: (StickerPackItem) -> Bool
let openSearch: () -> Void
let itemContext = StickerPaneSearchGlobalItemContext()
init(installPack: @escaping (ItemCollectionInfo, Bool) -> Void, openPack: @escaping (ItemCollectionInfo) -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool, openSearch: @escaping () -> Void) {
self.installPack = installPack
@ -95,7 +96,7 @@ private final class FeaturedPackEntry: Identifiable, Comparable {
interaction.installPack(info, !self.installed)
}, getItemIsPreviewed: { item in
return interaction.getItemIsPreviewed(item)
})
}, itemContext: interaction.itemContext)
}
}
@ -211,6 +212,8 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
private var canLoadMore: Bool = true
private var isLoadingMore: Bool = false
private var interaction: FeaturedInteraction?
private var enqueuedTransitions: [FeaturedTransition] = []
private var validLayout: ContainerViewLayout?
@ -292,14 +295,6 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
}
)
self.searchNode = FeaturedPaneSearchContentNode(
context: context,
theme: self.presentationData.theme,
strings: self.presentationData.strings,
inputNodeInteraction: inputNodeInteraction,
controller: controller,
sendSticker: sendSticker
)
self.searchNode?.updateActivity = { [weak self] activity in
self?.controller?.searchNavigationNode?.setActivity(activity)
}
@ -416,6 +411,17 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
openSearch: {
}
)
self.interaction = interaction
self.searchNode = FeaturedPaneSearchContentNode(
context: context,
theme: self.presentationData.theme,
strings: self.presentationData.strings,
inputNodeInteraction: inputNodeInteraction,
controller: controller,
sendSticker: sendSticker,
itemContext: interaction.itemContext
)
let previousEntries = Atomic<[FeaturedEntry]?>(value: nil)
let context = self.context
@ -670,6 +676,16 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
}))
}
func inFocusUpdated(isInFocus: Bool) {
self.interaction?.itemContext.canPlayMedia = isInFocus
self.gridNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? StickerPaneSearchGlobalItemNode {
itemNode.updateCanPlayMedia()
}
}
self.searchNode?.updateCanPlayMedia()
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
let firstTime = self.validLayout == nil
@ -817,6 +833,8 @@ final class FeaturedStickersScreen: ViewController {
private func updatePresentationData() {
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
self.searchNavigationNode?.updatePresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings)
self.controllerNode.updatePresentationData(presentationData: presentationData)
@ -847,6 +865,10 @@ final class FeaturedStickersScreen: ViewController {
super.viewDidAppear(animated)
}
override public func inFocusUpdated(isInFocus: Bool) {
self.controllerNode.inFocusUpdated(isInFocus: isInFocus)
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
@ -990,7 +1012,7 @@ private enum FeaturedSearchEntry: Identifiable, Comparable {
}
}
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction) -> GridItem {
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, itemContext: StickerPaneSearchGlobalItemContext) -> GridItem {
switch self {
case let .sticker(_, code, stickerItem, theme):
return StickerPaneSearchStickerItem(account: account, code: code, stickerItem: stickerItem, inputNodeInteraction: inputNodeInteraction, theme: theme, selected: { node, rect in
@ -1003,7 +1025,7 @@ private enum FeaturedSearchEntry: Identifiable, Comparable {
interaction.install(info, topItems, !installed)
}, getItemIsPreviewed: { item in
return interaction.getItemIsPreviewed(item)
})
}, itemContext: itemContext)
}
}
}
@ -1018,7 +1040,7 @@ private struct FeaturedSearchGridTransition {
let animated: Bool
}
private func preparedFeaturedSearchEntryTransition(account: Account, theme: PresentationTheme, strings: PresentationStrings, from fromEntries: [FeaturedSearchEntry], to toEntries: [FeaturedSearchEntry], interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction) -> FeaturedSearchGridTransition {
private func preparedFeaturedSearchEntryTransition(account: Account, theme: PresentationTheme, strings: PresentationStrings, from fromEntries: [FeaturedSearchEntry], to toEntries: [FeaturedSearchEntry], interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, itemContext: StickerPaneSearchGlobalItemContext) -> FeaturedSearchGridTransition {
let stationaryItems: GridNodeStationaryItems = .none
let scrollToItem: GridNodeScrollToItem? = nil
var animated = false
@ -1027,8 +1049,8 @@ private func preparedFeaturedSearchEntryTransition(account: Account, theme: Pres
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices
let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction), previousIndex: $0.2) }
let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction)) }
let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction, itemContext: itemContext), previousIndex: $0.2) }
let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction, itemContext: itemContext)) }
let firstIndexInSectionOffset = 0
@ -1041,6 +1063,7 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode {
private var interaction: StickerPaneSearchInteraction?
private weak var controller: FeaturedStickersScreen?
private let sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?
private let itemContext: StickerPaneSearchGlobalItemContext
private var theme: PresentationTheme
private var strings: PresentationStrings
@ -1073,11 +1096,12 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode {
return !self.gridNode.isHidden
}
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, inputNodeInteraction: ChatMediaInputNodeInteraction, controller: FeaturedStickersScreen, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?) {
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, inputNodeInteraction: ChatMediaInputNodeInteraction, controller: FeaturedStickersScreen, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, itemContext: StickerPaneSearchGlobalItemContext) {
self.context = context
self.inputNodeInteraction = inputNodeInteraction
self.controller = controller
self.sendSticker = sendSticker
self.itemContext = itemContext
self.theme = theme
self.strings = strings
@ -1405,7 +1429,7 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode {
}
let previousEntries = strongSelf.currentEntries.swap(entries)
let transition = preparedFeaturedSearchEntryTransition(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, from: previousEntries ?? [], to: entries, interaction: interaction, inputNodeInteraction: strongSelf.inputNodeInteraction)
let transition = preparedFeaturedSearchEntryTransition(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, from: previousEntries ?? [], to: entries, interaction: interaction, inputNodeInteraction: strongSelf.inputNodeInteraction, itemContext: strongSelf.itemContext)
strongSelf.enqueueTransition(transition)
if displayResults {
@ -1515,4 +1539,12 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode {
return super.hitTest(point, with: event)
}
func updateCanPlayMedia() {
self.gridNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? StickerPaneSearchGlobalItemNode {
itemNode.updateCanPlayMedia()
}
}
}
}

View File

@ -431,6 +431,7 @@ public func pollResultsController(context: AccountContext, messageId: MessageId,
}
controller.isOpaqueWhenInOverlay = true
controller.blocksBackgroundWhenInOverlay = true
controller.acceptsFocusWhenInOverlay = true
return controller
}

View File

@ -104,13 +104,15 @@ private enum StickerSearchEntry: Identifiable, Comparable {
interaction.sendSticker(.standalone(media: stickerItem.file), node, rect)
})
case let .global(_, info, topItems, installed, topSeparator):
let itemContext = StickerPaneSearchGlobalItemContext()
itemContext.canPlayMedia = true
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)
}, getItemIsPreviewed: { item in
return interaction.getItemIsPreviewed(item)
})
}, itemContext: itemContext)
}
}
}

View File

@ -32,6 +32,9 @@ final class StickerPaneSearchGlobalSection: GridSection {
}
}
final class StickerPaneSearchGlobalItemContext {
var canPlayMedia: Bool = false
}
final class StickerPaneSearchGlobalItem: GridItem {
let account: Account
@ -48,13 +51,14 @@ final class StickerPaneSearchGlobalItem: GridItem {
let open: () -> Void
let install: () -> Void
let getItemIsPreviewed: (StickerPackItem) -> Bool
let itemContext: StickerPaneSearchGlobalItemContext
let section: GridSection? = StickerPaneSearchGlobalSection()
var fillsRowWithHeight: CGFloat? {
return self.grid ? nil : (128.0 + (self.topSeparator ? 12.0 : 0.0))
}
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) {
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, itemContext: StickerPaneSearchGlobalItemContext) {
self.account = account
self.theme = theme
self.strings = strings
@ -69,6 +73,7 @@ final class StickerPaneSearchGlobalItem: GridItem {
self.open = open
self.install = install
self.getItemIsPreviewed = getItemIsPreviewed
self.itemContext = itemContext
}
func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode {
@ -109,19 +114,37 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
private let preloadedStickerPackThumbnailDisposable = MetaDisposable()
private var preloadedThumbnail = false
private var canPlay = false
private var canPlayMedia: Bool = false {
didSet {
if self.canPlayMedia != oldValue {
self.updatePlayback()
}
}
}
override var isVisibleInGrid: Bool {
didSet {
if oldValue != self.isVisibleInGrid {
for node in self.itemNodes {
node.visibility = self.isVisibleInGrid
}
self.updatePlayback()
}
}
}
private func updatePlayback() {
let canPlay = self.canPlayMedia && self.isVisibleInGrid
if canPlay != self.canPlay {
self.canPlay = canPlay
for node in self.itemNodes {
node.visibility = self.canPlay
}
if let item = self.item, self.isVisibleInGrid, !self.preloadedThumbnail {
self.preloadedThumbnail = true
if let item = self.item, self.isVisibleInGrid, !self.preloadedThumbnail {
self.preloadedThumbnail = true
self.preloadedStickerPackThumbnailDisposable.set(preloadedStickerPackThumbnail(account: item.account, info: item.info, items: item.topItems).start())
}
self.preloadedStickerPackThumbnailDisposable.set(preloadedStickerPackThumbnail(account: item.account, info: item.info, items: item.topItems).start())
}
}
}
@ -241,6 +264,14 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
self.updatePreviewing(animated: false)
}
func updateCanPlayMedia() {
guard let item = self.item else {
return
}
self.canPlayMedia = item.itemContext.canPlayMedia
}
override func updateLayout(item: GridItem, size: CGSize, isVisible: Bool, synchronousLoads: Bool) {
guard let item = self.item else {
return
@ -356,7 +387,7 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
node = strongSelf.itemNodes[i]
} else {
node = TrendingTopItemNode()
node.visibility = strongSelf.isVisibleInGrid
node.visibility = strongSelf.canPlay
strongSelf.itemNodes.append(node)
strongSelf.addSubnode(node)
}
@ -376,6 +407,8 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
strongSelf.itemNodes.remove(at: i)
}
}
self.canPlayMedia = item.itemContext.canPlayMedia
}
@objc func installPressed() {

View File

@ -357,6 +357,7 @@ public final class TooltipScreen: ViewController {
private var validLayout: ContainerViewLayout?
private var isDismissed: Bool = false
public var willBecomeDismissed: ((TooltipScreen) -> Void)?
public var becameDismissed: ((TooltipScreen) -> Void)?
public init(text: String, textEntities: [MessageTextEntity] = [], icon: TooltipScreen.Icon?, location: TooltipScreen.Location, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openActiveTextItem: @escaping (TooltipActiveTextItem, TooltipActiveTextAction) -> Void = { _, _ in }) {
@ -381,7 +382,7 @@ public final class TooltipScreen: ViewController {
self.controllerNode.animateIn()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 15.0, execute: { [weak self] in
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5.0, execute: { [weak self] in
self?.dismiss()
})
}
@ -418,6 +419,7 @@ public final class TooltipScreen: ViewController {
return
}
self.isDismissed = true
self.willBecomeDismissed?(self)
self.controllerNode.animateOut(completion: { [weak self] in
guard let strongSelf = self else {
return