diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 9faeb8ac77..3575e2c736 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -3118,8 +3118,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } - public func scrollToStories() { + public func scrollToStories(peerId: EnginePeer.Id? = nil) { self.chatListDisplayNode.scrollToStories(animated: false) + + if let peerId, let componentView = self.chatListHeaderView(), let storyPeerListView = componentView.storyPeerListView() { + storyPeerListView.ensureItemVisible(peerId: peerId) + } } public func scrollToStoriesAnimated() { diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift index 68e3e61e6c..dadb6d7ec4 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -439,6 +439,13 @@ private final class ItemNode: ASDisplayNode { return deleteButtonNode.view } } + + if self.buttonNode.isUserInteractionEnabled { + if let result = self.buttonNode.view.hitTest(self.view.convert(point, to: self.buttonNode.view), with: event) { + return result + } + } + return super.hitTest(point, with: event) } } diff --git a/submodules/SparseItemGrid/Sources/SparseItemGrid.swift b/submodules/SparseItemGrid/Sources/SparseItemGrid.swift index f2e50f94a0..ef208b69f4 100644 --- a/submodules/SparseItemGrid/Sources/SparseItemGrid.swift +++ b/submodules/SparseItemGrid/Sources/SparseItemGrid.swift @@ -820,7 +820,7 @@ public final class SparseItemGrid: ASDisplayNode { self.scrollView.setContentOffset(CGPoint(x: 0.0, y: contentOffset), animated: false) } - func ensureItemVisible(index: Int) { + func ensureItemVisible(index: Int, anyAmount: Bool) { guard let layout = self.layout, let _ = self.items else { return } @@ -830,8 +830,14 @@ public final class SparseItemGrid: ASDisplayNode { let itemFrame = layout.frame(at: index) let visibleBounds = self.scrollView.bounds - if itemFrame.intersects(visibleBounds) { - return + if anyAmount { + if itemFrame.intersects(visibleBounds) { + return + } + } else { + if visibleBounds.contains(itemFrame) { + return + } } var contentOffset: CGFloat @@ -1936,11 +1942,11 @@ public final class SparseItemGrid: ASDisplayNode { currentViewport.scrollToItem(at: index) } - public func ensureItemVisible(index: Int) { + public func ensureItemVisible(index: Int, anyAmount: Bool = true) { guard let currentViewport = self.currentViewport else { return } - currentViewport.ensureItemVisible(index: index) + currentViewport.ensureItemVisible(index: index, anyAmount: anyAmount) } public func scrollToTop() -> Bool { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index 0715ce9f37..34140598dd 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -1000,6 +1000,15 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId updatedItems.append(updatedItem) } transaction.setStoryItems(peerId: toPeerId, items: items) + + if let peer = transaction.getPeer(toPeerId) as? TelegramChannel, let storiesHidden = peer.storiesHidden { + let subscriptionsKey: PostboxStorySubscriptionsKey = storiesHidden ? .hidden : .filtered + var (state, peerIds) = transaction.getAllStorySubscriptions(key: subscriptionsKey) + if !peerIds.contains(toPeerId) { + peerIds.append(toPeerId) + } + transaction.replaceAllStorySubscriptions(key: .filtered, state: state, peerIds: peerIds) + } } id = idValue diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 957a100c32..edc2ac5dd9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -953,6 +953,10 @@ public extension TelegramEngine { return lhs.peer.id < rhs.peer.id }) + if !isHidden { + assert(true) + } + return EngineStorySubscriptions(accountItem: accountItem, items: items, hasMoreToken: hasMoreToken) } } diff --git a/submodules/TelegramCore/Sources/UpdatePeers.swift b/submodules/TelegramCore/Sources/UpdatePeers.swift index e53908e1c5..8708be13c5 100644 --- a/submodules/TelegramCore/Sources/UpdatePeers.swift +++ b/submodules/TelegramCore/Sources/UpdatePeers.swift @@ -126,7 +126,7 @@ func _internal_updatePeerIsContact(transaction: Transaction, user: TelegramUser, } } -private func _internal_updateChannelMembership(transaction: Transaction, channel: TelegramChannel, isMember: Bool) { +private func _internal_updateChannelMembership(transaction: Transaction, channel: TelegramChannel, isMember: Bool, justJoined: Bool) { if isMember, let storiesHidden = channel.storiesHidden { if storiesHidden { if transaction.storySubscriptionsContains(key: .filtered, peerId: channel.id) { @@ -155,6 +155,10 @@ private func _internal_updateChannelMembership(transaction: Transaction, channel } } } + + if justJoined { + _internal_addSynchronizePeerStoriesOperation(peerId: channel.id, transaction: transaction) + } } else { if transaction.storySubscriptionsContains(key: .filtered, peerId: channel.id) { var (state, peerIds) = transaction.getAllStorySubscriptions(key: .filtered) @@ -191,7 +195,7 @@ public func updatePeersCustom(transaction: Transaction, peers: [Peer], update: ( if let updated = updated as? TelegramChannel { let isMember = updated.participationStatus == .member if isMember != wasMember || updated.storiesHidden != wasHidden { - _internal_updateChannelMembership(transaction: transaction, channel: updated, isMember: isMember) + _internal_updateChannelMembership(transaction: transaction, channel: updated, isMember: isMember, justJoined: previous == nil || wasHidden == nil) } } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift index 36c88fb6a3..cbd641d986 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift @@ -481,21 +481,29 @@ private final class CaptureProtectedItemLayer: AVSampleBufferDisplayLayer, ItemL } private final class ItemTransitionView: UIView { - private weak var itemLayer: ItemLayer? + private weak var itemLayer: CALayer? private var copyDurationLayer: SimpleLayer? private var durationLayerBottomLeftPosition: CGPoint? - init(itemLayer: ItemLayer?) { + init(itemLayer: CALayer?) { self.itemLayer = itemLayer super.init(frame: CGRect()) if let itemLayer { - self.layer.contents = itemLayer.contents self.layer.contentsRect = itemLayer.contentsRect - if let durationLayer = itemLayer.durationLayer { + var durationLayer: CALayer? + if let itemLayer = itemLayer as? CaptureProtectedItemLayer { + durationLayer = itemLayer.durationLayer + self.layer.contents = itemLayer.getContents() + } else if let itemLayer = itemLayer as? ItemLayer { + durationLayer = itemLayer.durationLayer + self.layer.contents = itemLayer.contents + } + + if let durationLayer { let copyDurationLayer = SimpleLayer() copyDurationLayer.contents = durationLayer.contents copyDurationLayer.contentsRect = durationLayer.contentsRect @@ -1074,7 +1082,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr destinationView: self.view, transitionView: StoryContainerScreen.TransitionView( makeView: { [weak foundItemLayer] in - return ItemTransitionView(itemLayer: foundItemLayer as? ItemLayer) + return ItemTransitionView(itemLayer: foundItemLayer) }, updateView: { view, state, transition in (view as? ItemTransitionView)?.update(state: state, transition: transition) @@ -1104,10 +1112,15 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr return } if let itemId { + let anyAmount = self.itemInteraction.hiddenMedia.isEmpty self.itemInteraction.hiddenMedia = Set([itemId.id]) + if let items = self.items, let item = items.items.first(where: { $0.id == AnyHashable(itemId.id) }) { + self.itemGrid.ensureItemVisible(index: item.index, anyAmount: anyAmount) + } } else { self.itemInteraction.hiddenMedia = Set() } + self.updateHiddenItems() }) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index 90f30d29df..d05405eaf5 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -338,6 +338,7 @@ private final class StoryContainerScreenComponent: Component { private let backgroundEffectView: BlurredBackgroundView private let focusedItem = ValuePromise(nil, ignoreRepeated: true) + private var stateValue: StoryContentContextState? private var contentUpdatedDisposable: Disposable? private var stealthModeActiveUntilTimestamp: Int32? @@ -412,7 +413,7 @@ private final class StoryContainerScreenComponent: Component { self.layer.addSublayer(self.backgroundLayer) let horizontalPanRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in - guard let self, let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { + guard let self, let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { return [] } if let environment = self.environment, case .regular = environment.metrics.widthClass { @@ -430,7 +431,7 @@ private final class StoryContainerScreenComponent: Component { //TODO:move dismiss pan /*let verticalPanRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.dismissPanGesture(_:)), allowedDirections: { [weak self] point in - guard let self, let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { + guard let self, let component = self.component, let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { return [] } if let environment = self.environment, case .regular = environment.metrics.widthClass { @@ -460,7 +461,7 @@ private final class StoryContainerScreenComponent: Component { guard let self else { return false } - guard let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { + guard let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { return false } if !itemSetComponentView.allowsExternalGestures(point: touch.location(in: itemSetComponentView)) { @@ -480,7 +481,7 @@ private final class StoryContainerScreenComponent: Component { guard let self else { return false } - if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { + if let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { let itemLocation = self.convert(pinchLocation, to: itemSetComponentView) if itemSetComponentView.allowsExternalGestures(point: itemLocation) { @@ -498,7 +499,7 @@ private final class StoryContainerScreenComponent: Component { return } var pinchLocation = pinchLocation - if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { + if let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { pinchLocation = self.convert(pinchLocation, to: itemSetComponentView) } @@ -634,7 +635,7 @@ private final class StoryContainerScreenComponent: Component { } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - guard let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { + guard let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { return false } @@ -678,7 +679,7 @@ private final class StoryContainerScreenComponent: Component { private func updateHorizontalPan(translation: CGPoint) { var translation = translation - if var itemSetPanState = self.itemSetPanState, self.bounds.width > 0.0, let component = self.component, let stateValue = component.content.stateValue, let _ = stateValue.slice { + if var itemSetPanState = self.itemSetPanState, self.bounds.width > 0.0, let stateValue = self.stateValue, let _ = stateValue.slice { func rubberBandingOffset(offset: CGFloat, bandingStart: CGFloat) -> CGFloat { let bandedOffset = offset - bandingStart let range: CGFloat = 600.0 @@ -706,7 +707,7 @@ private final class StoryContainerScreenComponent: Component { if var itemSetPanState = self.itemSetPanState { var shouldDismiss = false - if let component = self.component, let stateValue = component.content.stateValue, let _ = stateValue.slice { + if let component = self.component, let stateValue = self.stateValue, let _ = stateValue.slice { var direction: StoryContentContextNavigation.PeerDirection? var mayDismiss = false if itemSetPanState.fraction <= -0.3 { @@ -787,7 +788,7 @@ private final class StoryContainerScreenComponent: Component { case .began: self.dismissAllTooltips() - if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { + if let component = self.component, let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { if itemSetComponentView.hasActiveDeactivateableInput() { itemSetComponentView.deactivateInput() @@ -806,7 +807,7 @@ private final class StoryContainerScreenComponent: Component { self.state?.updated(transition: .immediate) if translation.y < -40.0 { - if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { + if let component = self.component, let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { if let activateInputWhileDragging = itemSetComponentView.activateInputWhileDragging() { activateInputWhileDragging() @@ -830,7 +831,7 @@ private final class StoryContainerScreenComponent: Component { self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring))) self.environment?.controller()?.dismiss() } else if translation.y < -200.0 || (translation.y < -100.0 && velocity.y < -100.0) { - if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { + if let component = self.component, let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { if itemSetComponentView.activateInput() { updateState = false @@ -872,7 +873,7 @@ private final class StoryContainerScreenComponent: Component { return } let location = recognizer.location(in: recognizer.view) - if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let currentItemView = itemSetView.view.view as? StoryItemSetContainerComponent.View { + if let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let currentItemView = itemSetView.view.view as? StoryItemSetContainerComponent.View { if currentItemView.hasActiveDeactivateableInput() { currentItemView.deactivateInput() } else { @@ -893,7 +894,7 @@ private final class StoryContainerScreenComponent: Component { } if subview is ItemSetView { - if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], itemSetView === subview { + if let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], itemSetView === subview { if let result = subview.hitTest(self.convert(point, to: subview), with: event) { return result } @@ -934,7 +935,7 @@ private final class StoryContainerScreenComponent: Component { self.backgroundLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.28, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) self.backgroundEffectView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.28, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) - if let transitionIn = self.component?.transitionIn, let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { + if let transitionIn = self.component?.transitionIn, let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] { if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { itemSetComponentView.animateIn(transitionIn: transitionIn) } @@ -958,7 +959,7 @@ private final class StoryContainerScreenComponent: Component { return } if !value { - if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let currentItemView = itemSetView.view.view as? StoryItemSetContainerComponent.View { + if let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let currentItemView = itemSetView.view.view as? StoryItemSetContainerComponent.View { currentItemView.maybeDisplayReactionTooltip() } } @@ -971,7 +972,7 @@ private final class StoryContainerScreenComponent: Component { func animateOut(completion: @escaping () -> Void) { self.isAnimatingOut = true - if !self.dismissWithoutTransitionOut, let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View, let transitionOut = component.transitionOut(slice.peer.id, slice.item.storyItem.id) { + if !self.dismissWithoutTransitionOut, let component = self.component, let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View, let transitionOut = component.transitionOut(slice.peer.id, slice.item.storyItem.id) { self.state?.updated(transition: .immediate) let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut)) @@ -991,7 +992,7 @@ private final class StoryContainerScreenComponent: Component { focusedItemPromise.set(.single(nil)) }) } else { - if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let transitionOut = component.transitionOut(slice.peer.id, slice.item.storyItem.id) { + if let component = self.component, let stateValue = self.stateValue, let slice = stateValue.slice, let transitionOut = component.transitionOut(slice.peer.id, slice.item.storyItem.id) { transitionOut.completed() } @@ -1020,10 +1021,10 @@ private final class StoryContainerScreenComponent: Component { private func updateVolumeButtonMonitoring() { if self.volumeButtonsListener == nil { let buttonAction = { [weak self] in - guard let self, let component = self.component else { + guard let self else { return } - guard let slice = component.content.stateValue?.slice else { + guard let slice = self.stateValue?.slice else { return } var isSilentVideo = false @@ -1038,7 +1039,7 @@ private final class StoryContainerScreenComponent: Component { } if isSilentVideo { - if let slice = component.content.stateValue?.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let currentItemView = itemSetView.view.view as? StoryItemSetContainerComponent.View { + if let slice = self.stateValue?.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let currentItemView = itemSetView.view.view as? StoryItemSetContainerComponent.View { currentItemView.displayMutedVideoTooltip() } } else { @@ -1072,7 +1073,7 @@ private final class StoryContainerScreenComponent: Component { return } - if let stateValue = component.content.stateValue, let slice = stateValue.slice { + if let stateValue = self.stateValue, let slice = stateValue.slice { if case .next = direction, slice.nextItemId == nil, (slice.item.position == nil || slice.item.position == slice.totalCount - 1) { if stateValue.nextSlice == nil { environment.controller()?.dismiss() @@ -1194,10 +1195,15 @@ private final class StoryContainerScreenComponent: Component { guard let self else { return } + if self.isAnimatingOut || self.didAnimateOut { + return + } + + let stateValue = component.content.stateValue var focusedItemId: StoryId? var isVideo = false - if let slice = component.content.stateValue?.slice { + if let slice = stateValue?.slice { focusedItemId = StoryId(peerId: slice.peer.id, id: slice.item.storyItem.id) if case .file = slice.item.storyItem.media { isVideo = true @@ -1206,25 +1212,40 @@ private final class StoryContainerScreenComponent: Component { self.focusedItem.set(focusedItemId) self.contentWantsVolumeButtonMonitoring.set(isVideo) - if update { - if component.content.stateValue?.slice == nil { - self.environment?.controller()?.dismiss() - } else { - let startTime = CFAbsoluteTimeGetCurrent() - self.state?.updated(transition: .immediate) - print("update time: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms") + var hasItems = false + if let stateValue { + if stateValue.slice != nil { + hasItems = true } + } + + if !hasItems { + self.dismissWithoutTransitionOut = true + environment.controller()?.dismiss() } else { - DispatchQueue.main.async { [weak self] in - guard let self else { - return + self.stateValue = stateValue + + if update { + if self.stateValue?.slice == nil { + self.environment?.controller()?.dismiss() + } else { + let startTime = CFAbsoluteTimeGetCurrent() + self.state?.updated(transition: .immediate) + print("update time: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms") + } + } else { + DispatchQueue.main.async { [weak self] in + guard let self else { + return + } + self.state?.updated(transition: .immediate) } - self.state?.updated(transition: .immediate) } } } self.contentUpdatedDisposable?.dispose() + self.stateValue = component.content.stateValue self.contentUpdatedDisposable = (component.content.updated |> deliverOnMainQueue).start(next: { [weak self] _ in guard let self, let component = self.component else { @@ -1268,7 +1289,7 @@ private final class StoryContainerScreenComponent: Component { } if let pendingNavigationToItemId = self.pendingNavigationToItemId { - if let slice = component.content.stateValue?.slice, slice.peer.id == pendingNavigationToItemId.peerId { + if let slice = self.stateValue?.slice, slice.peer.id == pendingNavigationToItemId.peerId { if slice.item.storyItem.id == pendingNavigationToItemId.id { self.pendingNavigationToItemId = nil } @@ -1315,7 +1336,7 @@ private final class StoryContainerScreenComponent: Component { var currentSlices: [StoryContentContextState.FocusedSlice] = [] var focusedIndex: Int? - if let component = self.component, let stateValue = component.content.stateValue { + if let stateValue = self.stateValue { if let previousSlice = stateValue.previousSlice { currentSlices.append(previousSlice) } @@ -1458,7 +1479,7 @@ private final class StoryContainerScreenComponent: Component { guard let self else { return } - if let stateValue = component.content.stateValue, let slice = stateValue.slice { + if let stateValue = self.stateValue, let slice = stateValue.slice { if slice.nextItemId != nil { component.content.navigate(navigation: .item(.next)) } else if slice.previousItemId != nil { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index 095351bd41..0261802de2 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -215,7 +215,7 @@ final class StoryItemContentComponent: Component { videoNode.isHidden = true self.videoNode = videoNode - self.addSubview(videoNode.view) + self.insertSubview(videoNode.view, aboveSubview: self.imageView) videoNode.playbackCompleted = { [weak self] in guard let self else { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 4bdf5a82cb..7552cea851 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -444,6 +444,7 @@ public final class StoryItemSetContainerComponent: Component { var reactionContextNode: ReactionContextNode? weak var disappearingReactionContextNode: ReactionContextNode? var displayLikeReactions: Bool = false + var tempReactionsGesture: ContextGesture? var waitingForReactionAnimateOutToLike: MessageReaction.Reaction? weak var standaloneReactionAnimation: StandaloneReactionAnimation? @@ -1656,7 +1657,10 @@ public final class StoryItemSetContainerComponent: Component { externalViews: nil, expandFraction: footerExpandFraction, expandViewStats: { [weak self] in - guard let self else { + guard let self, let component = self.component else { + return + } + if self.viewLists[component.slice.item.storyItem.id] == nil { return } @@ -2025,6 +2029,13 @@ public final class StoryItemSetContainerComponent: Component { self.contextController?.dismiss() self.contextController = nil + if let standaloneReactionAnimation = self.standaloneReactionAnimation { + self.standaloneReactionAnimation = nil + standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in + standaloneReactionAnimation?.view.removeFromSuperview() + }) + } + if let inputPanelView = self.inputPanel.view { inputPanelView.layer.animatePosition( from: CGPoint(), @@ -2769,12 +2780,11 @@ public final class StoryItemSetContainerComponent: Component { self.performLikeAction() }, likeOptionsAction: !haveLikeOptions ? nil : { [weak self] sourceView, gesture in - gesture?.cancel() - guard let self else { + gesture?.cancel() return } - self.performLikeOptionsAction(sourceView: sourceView) + self.performLikeOptionsAction(sourceView: sourceView, gesture: gesture) }, inputModeAction: { [weak self] in guard let self else { @@ -3108,20 +3118,7 @@ public final class StoryItemSetContainerComponent: Component { self.isSearchActive = false self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring))) }, - expandViewStats: { [weak self] in - guard let self else { - return - } - - if self.viewListDisplayState == .hidden { - self.viewListDisplayState = .half - - self.preparingToDisplayViewList = true - self.updateScrolling(transition: .immediate) - self.preparingToDisplayViewList = false - - self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring))) - } + expandViewStats: { }, deleteAction: { [weak self] in guard let self, let component = self.component else { @@ -4160,6 +4157,26 @@ public final class StoryItemSetContainerComponent: Component { reactionContextNode.forceDark = true self.reactionContextNode = reactionContextNode + if let tempReactionsGesture = self.tempReactionsGesture { + tempReactionsGesture.externalUpdated = { [weak self] view, point in + guard let self, let view, let reactionContextNode = self.reactionContextNode else { + return + } + let presentationPoint = view.convert(point, to: reactionContextNode.view) + reactionContextNode.highlightGestureMoved(location: presentationPoint, hover: false) + } + tempReactionsGesture.externalEnded = { [weak self] viewAndPoint in + guard let self, let viewAndPoint, let reactionContextNode = self.reactionContextNode else { + return + } + let _ = viewAndPoint + /*let (view, point) = viewAndPoint + let presentationPoint = view.convert(point, to: reactionContextNode.view) + let _ = presentationPoint*/ + reactionContextNode.highlightGestureFinished(performAction: true) + } + } + reactionContextNode.reactionSelected = { [weak self] updateReaction, _ in guard let self else { return @@ -4553,7 +4570,7 @@ public final class StoryItemSetContainerComponent: Component { } else if let inputPanelFrameValue { component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrameValue.minY, contentFrame.maxY) } else { - component.externalState.derivedBottomInset = 0.0 + component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY } if !"".isEmpty { @@ -5332,12 +5349,14 @@ public final class StoryItemSetContainerComponent: Component { } } - private func performLikeOptionsAction(sourceView: UIView) { + private func performLikeOptionsAction(sourceView: UIView, gesture: ContextGesture?) { HapticFeedback().tap() self.displayLikeReactions = true + self.tempReactionsGesture = gesture self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) self.updateIsProgressPaused() + self.tempReactionsGesture = nil } func dismissAllTooltips() { diff --git a/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift b/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift index 937272a2c9..8b334c9356 100644 --- a/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent/Sources/StoryFooterPanelComponent.swift @@ -355,7 +355,7 @@ public final class StoryFooterPanelComponent: Component { } } - self.viewStatsButton.isEnabled = viewCount != 0 + self.viewStatsButton.isEnabled = viewCount != 0 && !component.isChannel var rightContentOffset: CGFloat = availableSize.width - 12.0 diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift index 085a4a0970..f1773e49e9 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift @@ -460,6 +460,15 @@ public final class StoryPeerListComponent: Component { }) } + public func ensureItemVisible(peerId: EnginePeer.Id) { + if let visibleItem = self.visibleItems[peerId], let itemView = visibleItem.view.view as? StoryPeerListItemComponent.View { + let itemFrame = itemView.frame.offsetBy(dx: self.scrollView.bounds.minX, dy: 0.0) + if !self.scrollView.bounds.contains(itemFrame.insetBy(dx: 20.0, dy: 0.0)) { + self.scrollView.scrollRectToVisible(itemFrame.insetBy(dx: -40.0, dy: 0.0), animated: false) + } + } + } + public func setLoadingItem(peerId: EnginePeer.Id, signal: Signal) { var applyLoadingItem = true self.loadingItemDisposable?.dispose() diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index e38ad40d13..7283981eb0 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -10012,7 +10012,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { cancelImpl?() })) - strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + //strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) return ActionDisposable { [weak controller] in Queue.mainQueue().async() { controller?.dismiss() diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift index 3efaf6a169..87b6378e2d 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift @@ -982,6 +982,29 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat paneTransition.updateFrame(node: pane.pane.node, frame: paneFrame) pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: true, transition: paneTransition) } + + var removeKeys: [PeerInfoPaneKey] = [] + for (key, paneNode) in self.pendingPanes { + if !availablePanes.contains(key) { + removeKeys.append(key) + paneNode.pane.node.removeFromSupernode() + } + } + for key in removeKeys { + self.pendingPanes.removeValue(forKey: key) + } + removeKeys.removeAll() + + for (key, paneNode) in self.currentPanes { + if !availablePanes.contains(key) { + removeKeys.append(key) + paneNode.node.removeFromSupernode() + } + } + for key in removeKeys { + self.currentPanes.removeValue(forKey: key) + } + if !self.didSetIsReady && data != nil { if let currentPaneKey = self.currentPaneKey, let currentPane = self.currentPanes[currentPaneKey] { self.didSetIsReady = true diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 47ad60579d..7aede0fcdc 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -382,6 +382,17 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } } + let target: Stories.PendingTarget + let targetPeerId: EnginePeer.Id + if let sendAsPeerId = options.sendAsPeerId { + target = .peer(sendAsPeerId) + targetPeerId = sendAsPeerId + } else { + target = .myStories + targetPeerId = context.account.peerId + } + storyTarget = target + let completionImpl: () -> Void = { [weak self] in guard let self else { return @@ -393,7 +404,11 @@ public final class TelegramRootController: NavigationController, TelegramRootCon |> take(1) |> timeout(0.25, queue: .mainQueue(), alternate: .single(true)) |> deliverOnMainQueue).start(completed: { [weak chatListController] in - chatListController?.scrollToStories() + guard let chatListController else { + return + } + + chatListController.scrollToStories(peerId: targetPeerId) Queue.mainQueue().justDispatch { commit({}) } @@ -405,14 +420,6 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } } - let target: Stories.PendingTarget - if let sendAsPeerId = options.sendAsPeerId { - target = .peer(sendAsPeerId) - } else { - target = .myStories - } - storyTarget = target - if let _ = self.chatListController as? ChatListControllerImpl { switch mediaResult { case let .image(image, dimensions):