diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 901e8e1bc8..d005b452a1 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -118,6 +118,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private var didAppear = false private var dismissSearchOnDisappear = false + public var onDidAppear: (() -> Void)? private var passcodeLockTooltipDisposable = MetaDisposable() private var didShowPasscodeLockTooltipController = false @@ -187,6 +188,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private(set) var orderedStorySubscriptions: EngineStorySubscriptions? private var displayedStoriesTooltip: Bool = false + public var hasStorySubscriptions: Bool { + if let rawStorySubscriptions = self.rawStorySubscriptions, !rawStorySubscriptions.items.isEmpty { + return true + } else { + return false + } + } + private var storyProgressDisposable: Disposable? private var storySubscriptionsDisposable: Disposable? private var preloadStorySubscriptionsDisposable: Disposable? @@ -1059,7 +1068,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let navigationController = strongSelf.navigationController as? NavigationController { let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .chatList(groupId: groupId), controlsHistoryPreload: false, enableDebugActions: false) chatListController.navigationPresentation = .master + #if DEBUG && false + navigationController.pushViewController(chatListController, animated: false, completion: {}) + chatListController.onDidAppear = { [weak chatListController] in + Queue.mainQueue().after(0.1, { + guard let chatListController else { + return + } + if chatListController.hasStorySubscriptions { + chatListController.scrollToStoriesAnimated() + } + }) + } + #else navigationController.pushViewController(chatListController) + #endif strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.clearHighlightAnimated(true) } } @@ -1813,12 +1836,18 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController |> filter { $0 } |> take(1)) } else { - self.storiesReady.set(.single(true)) + let signals: [Signal] = [ + self.primaryInfoReady.get(), + self.storiesReady.get() + ] - self.ready.set(combineLatest([ - self.chatListDisplayNode.mainContainerNode.ready, - self.primaryInfoReady.get() - ]) + if case .chatList(.archive) = self.location { + //signals.append(self.mainReady.get()) + } else { + self.storiesReady.set(.single(true)) + } + + self.ready.set(combineLatest(signals) |> map { values -> Bool in return !values.contains(where: { !$0 }) } @@ -1919,7 +1948,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.requestLayout(transition: transition) self.chatListDisplayNode.temporaryContentOffsetChangeTransition = nil - if rawStorySubscriptions.items.isEmpty { + if !shouldDisplayStoriesInChatListHeader(storySubscriptions: rawStorySubscriptions, isHidden: self.location == .chatList(groupId: .archive)) { self.chatListDisplayNode.scrollToTopIfStoriesAreExpanded() } @@ -2162,6 +2191,20 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } guard case .chatList(.root) = self.location else { + if !self.didSuggestLocalization { + self.didSuggestLocalization = true + + let _ = (self.chatListDisplayNode.mainContainerNode.ready + |> filter { $0 } + |> take(1) + |> timeout(0.5, queue: .mainQueue(), alternate: .single(true))).start(next: { [weak self] _ in + guard let self else { + return + } + self.onDidAppear?() + }) + } + return } @@ -2362,6 +2405,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } }) + + self.onDidAppear?() } self.chatListDisplayNode.mainContainerNode.addedVisibleChatsWithPeerIds = { [weak self] peerIds in @@ -2916,6 +2961,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.chatListDisplayNode.scrollToStories(animated: false) } + public func scrollToStoriesAnimated() { + self.chatListDisplayNode.scrollToStories(animated: true) + } + private func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { var tabContainerOffset: CGFloat = 0.0 if !self.displayNavigationBar { diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift index 7254df7504..f032bebb98 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift @@ -13,6 +13,7 @@ import ContextUI import AsyncDisplayKit import StoryContainerScreen import MultilineTextComponent +import HierarchyTrackingLayer private func calculateCircleIntersection(center: CGPoint, otherCenter: CGPoint, radius: CGFloat) -> (point1Angle: CGFloat, point2Angle: CGFloat)? { let distanceVector = CGPoint(x: otherCenter.x - center.x, y: otherCenter.y - center.y) @@ -124,7 +125,7 @@ private func calculateMergingCircleShape(center: CGPoint, leftCenter: CGPoint?, return path } -private final class StoryProgressLayer: SimpleLayer { +private final class StoryProgressLayer: HierarchyTrackingLayer { enum Value: Equatable { case indefinite case progress(Float) @@ -164,6 +165,13 @@ private final class StoryProgressLayer: SimpleLayer { self.indefiniteReplicatorLayer.instanceTransform = CATransform3DMakeRotation(CGFloat(angle), 0.0, 0.0, 1.0) self.indefiniteReplicatorLayer.transform = CATransform3DMakeRotation(-.pi / 2.0, 0.0, 0.0, 1.0) self.indefiniteReplicatorLayer.instanceDelay = 0.025 + + self.didEnterHierarchy = { [weak self] in + guard let self else { + return + } + self.updateAnimations(transition: .immediate) + } } override init(layer: Any) { @@ -180,35 +188,12 @@ private final class StoryProgressLayer: SimpleLayer { self.uploadProgressLayer.path = nil } - func update(size: CGSize, lineWidth: CGFloat, value: Value, transition: Transition) { - let params = Params( - size: size, - lineWidth: lineWidth, - value: value - ) - if self.currentParams == params { + func updateAnimations(transition: Transition) { + guard let params = self.currentParams else { return } - self.currentParams = params - let lineWidth: CGFloat = 2.0 - let bounds = CGRect(origin: .zero, size: size) - if self.uploadProgressLayer.path == nil { - let path = CGMutablePath() - path.addEllipse(in: CGRect(origin: CGPoint(x: lineWidth * 0.5, y: lineWidth * 0.5), size: CGSize(width: size.width - lineWidth, height: size.height - lineWidth))) - self.uploadProgressLayer.path = path - self.uploadProgressLayer.frame = bounds - } - - if self.indefiniteDashLayer.path == nil { - let path = CGMutablePath() - path.addEllipse(in: CGRect(origin: CGPoint(x: lineWidth * 0.5, y: lineWidth * 0.5), size: CGSize(width: size.width - lineWidth, height: size.height - lineWidth))) - self.indefiniteDashLayer.path = path - self.indefiniteReplicatorLayer.frame = bounds - self.indefiniteDashLayer.frame = bounds - } - - switch value { + switch params.value { case let .progress(progress): if self.indefiniteReplicatorLayer.superlayer != nil { self.indefiniteReplicatorLayer.removeFromSuperlayer() @@ -258,6 +243,37 @@ private final class StoryProgressLayer: SimpleLayer { } } } + + func update(size: CGSize, lineWidth: CGFloat, value: Value, transition: Transition) { + let params = Params( + size: size, + lineWidth: lineWidth, + value: value + ) + if self.currentParams == params { + return + } + self.currentParams = params + + let lineWidth: CGFloat = 2.0 + let bounds = CGRect(origin: .zero, size: size) + if self.uploadProgressLayer.path == nil { + let path = CGMutablePath() + path.addEllipse(in: CGRect(origin: CGPoint(x: lineWidth * 0.5, y: lineWidth * 0.5), size: CGSize(width: size.width - lineWidth, height: size.height - lineWidth))) + self.uploadProgressLayer.path = path + self.uploadProgressLayer.frame = bounds + } + + if self.indefiniteDashLayer.path == nil { + let path = CGMutablePath() + path.addEllipse(in: CGRect(origin: CGPoint(x: lineWidth * 0.5, y: lineWidth * 0.5), size: CGSize(width: size.width - lineWidth, height: size.height - lineWidth))) + self.indefiniteDashLayer.path = path + self.indefiniteReplicatorLayer.frame = bounds + self.indefiniteDashLayer.frame = bounds + } + + self.updateAnimations(transition: transition) + } } private var sharedAvatarBackgroundImage: UIImage? diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index fee628d90c..f64752cd0f 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -26,6 +26,7 @@ import AccountUtils import ContextUI import TelegramCallsUI import AuthorizationUI +import ChatListUI final class UnauthorizedApplicationContext { let sharedContext: SharedAccountContextImpl @@ -852,8 +853,56 @@ final class AuthorizedApplicationContext { } func openChatWithPeerId(peerId: PeerId, threadId: Int64?, messageId: MessageId? = nil, activateInput: Bool = false, storyId: StoryId?) { - if let _ = storyId { - self.rootController.chatListController?.openStories(peerId: peerId) + if let storyId { + if let chatListController = self.rootController.chatListController as? ChatListControllerImpl { + let _ = (chatListController.context.account.postbox.transaction { transaction -> Bool in + if let peer = transaction.getPeer(storyId.peerId) as? TelegramUser, let storiesHidden = peer.storiesHidden, storiesHidden { + return true + } else { + return false + } + } + |> deliverOnMainQueue).start(next: { [weak self] isArchived in + guard let self, let chatListController = self.rootController.chatListController as? ChatListControllerImpl else { + return + } + if isArchived { + if let navigationController = (chatListController.navigationController as? NavigationController) { + var viewControllers = navigationController.viewControllers + if let index = viewControllers.firstIndex(where: { c in + if let c = c as? ChatListControllerImpl { + if case .chatList(groupId: .archive) = c.location { + return true + } + } + return false + }) { + (viewControllers[index] as? ChatListControllerImpl)?.scrollToStories() + viewControllers.removeSubrange((index + 1) ..< viewControllers.count) + navigationController.setViewControllers(viewControllers, animated: false) + } else { + let archive = ChatListControllerImpl(context: chatListController.context, location: .chatList(groupId: .archive), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false) + archive.onDidAppear = { [weak archive] in + Queue.mainQueue().after(0.1, { + guard let archive else { + return + } + if archive.hasStorySubscriptions { + archive.scrollToStoriesAnimated() + } + }) + } + navigationController.pushViewController(archive, animated: false, completion: {}) + } + } + } else { + chatListController.scrollToStories() + if let navigationController = (chatListController.navigationController as? NavigationController) { + navigationController.popToRoot(animated: true) + } + } + }) + } } else { var visiblePeerId: PeerId? if let controller = self.rootController.topViewController as? ChatControllerImpl, controller.chatLocation.peerId == peerId, controller.chatLocation.threadId == threadId {