diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 83cf05cc27..7071f7a3b2 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -299,6 +299,7 @@ public enum ResolvedUrl { case invoice(slug: String, invoice: TelegramMediaInvoice?) case premiumOffer(reference: String?) case chatFolder(slug: String) + case story(peerId: PeerId, id: Int32) } public enum NavigateToChatKeepStack { diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 7c5717ab87..2a547c3f71 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -326,9 +326,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController case let .known(offset): let isFirstFilter = strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.chatListFilter == strongSelf.chatListDisplayNode.mainContainerNode.availableFilters.first?.filter - if offset <= navigationBarSearchContentHeight + 1.0 && strongSelf.chatListDisplayNode.inlineStackContainerNode != nil { + if offset <= ChatListNavigationBar.searchScrollHeight + 1.0 && strongSelf.chatListDisplayNode.inlineStackContainerNode != nil { strongSelf.setInlineChatList(location: nil) - } else if offset <= navigationBarSearchContentHeight + 1.0 && !isFirstFilter { + } else if offset <= ChatListNavigationBar.searchScrollHeight + 1.0 && !isFirstFilter { let firstFilter = strongSelf.chatListDisplayNode.effectiveContainerNode.availableFilters.first ?? .all let targetTab: ChatListFilterTabEntryId switch firstFilter { @@ -1244,7 +1244,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - let storyContent = StoryContentContextImpl(context: self.context, focusedPeerId: peerId) + let storyContent = StoryContentContextImpl(context: self.context, includeHidden: false, focusedPeerId: peerId) let _ = (storyContent.state |> filter { $0.slice != nil } |> take(1) @@ -1696,7 +1696,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.displayNodeDidLoad() if case .chatList(.root) = self.location { - self.preloadStorySubscriptionsDisposable = (self.context.engine.messages.preloadStorySubscriptions() + self.preloadStorySubscriptionsDisposable = (self.context.engine.messages.preloadStorySubscriptions(includeHidden: false) |> deliverOnMainQueue).start(next: { [weak self] resources in guard let self else { return @@ -1729,7 +1729,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.preloadStoryResourceDisposables.removeValue(forKey: id) } }) - self.storySubscriptionsDisposable = (self.context.engine.messages.storySubscriptions() + self.storySubscriptionsDisposable = (self.context.engine.messages.storySubscriptions(includeHidden: false) |> deliverOnMainQueue).start(next: { [weak self] storySubscriptions in guard let self else { return @@ -2313,10 +2313,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.validLayout = layout - if let searchTabsNode = self.searchTabsNode { - searchTabsNode.bounds.origin = CGPoint(x: 0.0, y: (layout.statusBarHeight ?? 0.0) + navigationBarSearchContentHeight + 44.0) - } - self.updateLayout(layout: layout, transition: transition) if layout.inVoiceOver != wasInVoiceOver { @@ -2329,7 +2325,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - let storyContent = StoryContentContextImpl(context: self.context, focusedPeerId: peer?.id) + let storyContent = StoryContentContextImpl(context: self.context, includeHidden: false, focusedPeerId: peer?.id) let _ = (storyContent.state |> take(1) |> deliverOnMainQueue).start(next: { [weak self] storyContentState in @@ -2424,15 +2420,20 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) }) }))) - items.append(.action(ContextMenuActionItem(text: "Mute", icon: { theme in + /*items.append(.action(ContextMenuActionItem(text: "Mute", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unmute"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.default) - }))) + })))*/ items.append(.action(ContextMenuActionItem(text: "Archive", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor) - }, action: { _, f in - f(.default) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + guard let self else { + return + } + self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: true) }))) } diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index c949ad3061..bb45f15011 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -185,7 +185,7 @@ private final class ChatListShimmerNode: ASDisplayNode { let chatListPresentationData = ChatListPresentationData(theme: presentationData.theme, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true) - let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let timestamp1: Int32 = 100000 let peers: [EnginePeer.Id: EnginePeer] = [:] let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in @@ -376,7 +376,7 @@ private final class ChatListContainerItemNode: ASDisplayNode { self.listNode = ChatListNode(context: context, location: location, chatListFilter: filter, previewing: previewing, fillPreloadItems: controlsHistoryPreload, mode: chatListMode, theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: animationCache, animationRenderer: animationRenderer, disableAnimations: true, isInlineMode: isInlineMode) if let controller, case .chatList(groupId: .root) = controller.location { - self.listNode.scrollHeightTopInset = navigationBarSearchContentHeight + 79.0 + self.listNode.scrollHeightTopInset = ChatListNavigationBar.searchScrollHeight + ChatListNavigationBar.storiesScrollHeight } super.init() @@ -838,6 +838,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele previousItemNode.listNode.openStories = nil previousItemNode.listNode.addedVisibleChatsWithPeerIds = nil previousItemNode.listNode.didBeginSelectingChats = nil + previousItemNode.listNode.canExpandHiddenItems = nil previousItemNode.accessibilityElementsHidden = true } @@ -914,7 +915,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele if itemNode.listNode.isTracking { if case let .known(value) = offset { if !self.storiesUnlocked { - if value < -50.0 { + if value < -40.0 { self.storiesUnlocked = true DispatchQueue.main.async { [weak self] in guard let self else { @@ -935,7 +936,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele } else if self.storiesUnlocked { switch offset { case let .known(value): - if value >= 79.0 { + if value >= ChatListNavigationBar.storiesScrollHeight { self.storiesUnlocked = false self.onStoriesLockedUpdated?(false) } @@ -950,7 +951,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele } switch self.currentItemNode.visibleContentOffset() { case let .known(value): - if value > 79.0 { + if value > ChatListNavigationBar.storiesScrollHeight { if self.storiesUnlocked { self.storiesUnlocked = false @@ -986,6 +987,12 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele itemNode.listNode.didBeginSelectingChats = { [weak self] in self?.didBeginSelectingChats?() } + itemNode.listNode.canExpandHiddenItems = { [weak self] in + guard let self, let canExpandHiddenItems = self.canExpandHiddenItems else { + return false + } + return canExpandHiddenItems() + } self.currentItemStateValue.set(itemNode.listNode.state |> map { state in let filterId: Int32? @@ -1047,6 +1054,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele var openStories: ((EnginePeer.Id) -> Void)? var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)? var didBeginSelectingChats: (() -> Void)? + var canExpandHiddenItems: (() -> Bool)? public var displayFilterLimit: (() -> Void)? public init(context: AccountContext, controller: ChatListControllerImpl?, location: ChatListControllerLocation, chatListMode: ChatListNodeMode = .chatList(appendContacts: true), previewing: Bool, controlsHistoryPreload: Bool, isInlineMode: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) { @@ -1715,8 +1723,23 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { guard let self else { return } - //self.controller?.requestLayout(transition: .immediate) - self.controller?.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + if isLocked { + self.controller?.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + } else { + self.controller?.requestLayout(transition: .immediate) + } + } + + self.mainContainerNode.canExpandHiddenItems = { [weak self] in + guard let self, let controller = self.controller else { + return false + } + + if let storySubscriptions = controller.storySubscriptions, !storySubscriptions.items.isEmpty, !self.mainContainerNode.storiesUnlocked { + return false + } else { + return true + } } let inlineContentPanRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.inlineContentPanGesture(_:)), allowedDirections: { [weak self] _ in @@ -1852,6 +1875,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { secondaryContent: headerContent?.secondaryContent, secondaryTransition: self.inlineStackContainerTransitionFraction, storySubscriptions: self.controller?.storySubscriptions, + storiesIncludeHidden: false, uploadProgress: self.controller?.storyUploadProgress, tabsNode: tabsNode, tabsNodeIsSearch: tabsNodeIsSearch, @@ -1948,7 +1972,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { var storiesInset = storiesInset let navigationBarLayout = self.updateNavigationBar(layout: layout, transition: transition) - self.mainContainerNode.initialScrollingOffset = navigationBarSearchContentHeight + navigationBarLayout.storiesInset + self.mainContainerNode.initialScrollingOffset = ChatListNavigationBar.searchScrollHeight + navigationBarLayout.storiesInset navigationBarHeight = navigationBarLayout.navigationHeight visualNavigationHeight = navigationBarLayout.navigationHeight @@ -2146,8 +2170,8 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { self.mainContainerNode.accessibilityElementsHidden = false self.inlineStackContainerNode?.accessibilityElementsHidden = false - return { [weak self] in - if let strongSelf = self, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout { + return { [weak self, weak placeholderNode] in + if let strongSelf = self, let placeholderNode, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout { searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated) searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) @@ -2242,21 +2266,21 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { return true } else { let searchScrollOffset = clippedScrollOffset - navigationBarComponentView.effectiveStoriesInsetHeight - if searchScrollOffset > 0.0 && searchScrollOffset < navigationBarSearchContentHeight { - if searchScrollOffset < navigationBarSearchContentHeight * 0.5 { + if searchScrollOffset > 0.0 && searchScrollOffset < ChatListNavigationBar.searchScrollHeight { + if searchScrollOffset < ChatListNavigationBar.searchScrollHeight * 0.5 { let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight, animated: true) } else { - let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight + navigationBarSearchContentHeight, animated: true) + let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight + ChatListNavigationBar.searchScrollHeight, animated: true) } return true } } } else { - if clippedScrollOffset > 0.0 && clippedScrollOffset < navigationBarSearchContentHeight { - if clippedScrollOffset < navigationBarSearchContentHeight * 0.5 { + if clippedScrollOffset > 0.0 && clippedScrollOffset < ChatListNavigationBar.searchScrollHeight { + if clippedScrollOffset < ChatListNavigationBar.searchScrollHeight * 0.5 { let _ = listView.scrollToOffsetFromTop(0.0, animated: true) } else { - let _ = listView.scrollToOffsetFromTop(navigationBarSearchContentHeight, animated: true) + let _ = listView.scrollToOffsetFromTop(ChatListNavigationBar.searchScrollHeight, animated: true) } return true } diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index d17371f72f..510ffaaffe 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -3394,7 +3394,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { let chatListPresentationData = ChatListPresentationData(theme: presentationData.theme, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true) - let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let timestamp1: Int32 = 100000 var peers: [EnginePeer.Id: EnginePeer] = [:] peers[peer1.id] = peer1 diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index ff1e73fcbb..c8a0e0e78f 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -19,6 +19,7 @@ import MultiAnimationRenderer import Postbox import ChatFolderLinkPreviewScreen import StoryContainerScreen +import ChatListHeaderComponent public enum ChatListNodeMode { case chatList(appendContacts: Bool) @@ -1154,6 +1155,8 @@ public final class ChatListNode: ListView { public var isEmptyUpdated: ((ChatListNodeEmptyState, Bool, ContainedViewLayoutTransition) -> Void)? private var currentIsEmptyState: ChatListNodeEmptyState? + public var canExpandHiddenItems: (() -> Bool)? + public var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)? private let currentRemovingItemId = Atomic(value: nil) @@ -1210,7 +1213,7 @@ public final class ChatListNode: ListView { self.theme = theme - self.scrollHeightTopInset = navigationBarSearchContentHeight + self.scrollHeightTopInset = ChatListNavigationBar.searchScrollHeight super.init() @@ -2379,11 +2382,11 @@ public final class ChatListNode: ListView { } } if !isHiddenItemVisible && strongSelf.currentState.hiddenItemShouldBeTemporaryRevealed { - strongSelf.updateState { state in + /*strongSelf.updateState { state in var state = state state.hiddenItemShouldBeTemporaryRevealed = false return state - } + }*/ } } } @@ -2717,6 +2720,7 @@ public final class ChatListNode: ListView { } } var startedScrollingAtUpperBound = false + var startedScrollingWithCanExpandHiddenItems = false self.beganInteractiveDragging = { [weak self] _ in guard let strongSelf = self else { @@ -2728,6 +2732,13 @@ public final class ChatListNode: ListView { case let .known(value): startedScrollingAtUpperBound = value <= 0.0 } + + if let canExpandHiddenItems = strongSelf.canExpandHiddenItems { + startedScrollingWithCanExpandHiddenItems = canExpandHiddenItems() + } else { + startedScrollingWithCanExpandHiddenItems = true + } + if strongSelf.currentState.peerIdWithRevealedOptions != nil { strongSelf.updateState { state in var state = state @@ -2784,7 +2795,7 @@ public final class ChatListNode: ListView { atTop = false case let .known(value): atTop = value <= 0.0 - if startedScrollingAtUpperBound && strongSelf.isTracking { + if startedScrollingAtUpperBound && startedScrollingWithCanExpandHiddenItems && strongSelf.isTracking { revealHiddenItems = value <= -60.0 } } diff --git a/submodules/ContactListUI/BUILD b/submodules/ContactListUI/BUILD index 54e01e9ff0..5cdac1cefe 100644 --- a/submodules/ContactListUI/BUILD +++ b/submodules/ContactListUI/BUILD @@ -36,7 +36,12 @@ swift_library( "//submodules/AnimatedStickerNode:AnimatedStickerNode", "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", "//submodules/QrCodeUI:QrCodeUI", - "//submodules/LocalizedPeerData:LocalizedPeerData" + "//submodules/LocalizedPeerData:LocalizedPeerData", + "//submodules/TelegramUI/Components/Stories/StoryContainerScreen", + "//submodules/TelegramUI/Components/Stories/StoryContentComponent", + "//submodules/TelegramUI/Components/Stories/StoryPeerListComponent", + "//submodules/TelegramUI/Components/ChatListTitleView", + "//submodules/ComponentFlow", ], visibility = [ "//visibility:public", diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 7bfa65ab5b..d131e351cf 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -718,7 +718,7 @@ public final class ContactListNode: ASDisplayNode { private var indexSections: [String]? private var queuedTransitions: [ContactsListNodeTransition] = [] - private var validLayout: (ContainerViewLayout, UIEdgeInsets)? + private var validLayout: (ContainerViewLayout, UIEdgeInsets, CGFloat)? private var _ready = ValuePromise() public var ready: Signal { @@ -1369,8 +1369,8 @@ public final class ContactListNode: ASDisplayNode { } }) - if let (validLayout, headerInsets) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(validLayout, headerInsets: headerInsets, transition: .immediate) + if let (validLayout, headerInsets, storiesInset) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(validLayout, headerInsets: headerInsets, storiesInset: storiesInset, transition: .immediate) } } } @@ -1433,9 +1433,12 @@ public final class ContactListNode: ASDisplayNode { } } - public func containerLayoutUpdated(_ layout: ContainerViewLayout, headerInsets: UIEdgeInsets, transition: ContainedViewLayoutTransition) { + private var previousStoriesInset: CGFloat? + public var ignoreStoryInsetAdjustment: Bool = false + + public func containerLayoutUpdated(_ layout: ContainerViewLayout, headerInsets: UIEdgeInsets, storiesInset: CGFloat, transition: ContainedViewLayoutTransition) { let hadValidLayout = self.validLayout != nil - self.validLayout = (layout, headerInsets) + self.validLayout = (layout, headerInsets, storiesInset) var insets = layout.insets(options: [.input]) insets.left = layout.safeInsets.left @@ -1446,12 +1449,22 @@ public final class ContactListNode: ASDisplayNode { headerInsets.top -= navigationBarSearchContentHeight } + var additionalScrollDistance: CGFloat = 0.0 + + if let previousStoriesInset = self.previousStoriesInset { + if self.ignoreStoryInsetAdjustment { + } else { + additionalScrollDistance += previousStoriesInset - storiesInset + } + } + self.previousStoriesInset = storiesInset + transition.updateFrame(node: self.listNode, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)) let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, headerInsets: headerInsets, duration: duration, curve: curve) - self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, additionalScrollDistance: additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) if let indexSections = self.indexSections { var insets = layout.insets(options: [.input]) if let inputHeight = layout.inputHeight { @@ -1506,7 +1519,7 @@ public final class ContactListNode: ASDisplayNode { options.insert(.AnimateCrossfade) } } - if let (layout, _) = self.validLayout { + if let (layout, _, _) = self.validLayout { self.indexSections = transition.indexSections var insets = layout.insets(options: [.input]) diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index 00b02ea55d..10329ebdae 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -19,18 +19,21 @@ import AppBundle import StickerResources import ContextUI import QrCodeUI +import StoryContainerScreen +import StoryContentComponent +import ChatListHeaderComponent private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { private let controller: ViewController - private let sourceNode: ContextReferenceContentNode + private let sourceView: UIView - init(controller: ViewController, sourceNode: ContextReferenceContentNode) { + init(controller: ViewController, sourceView: UIView) { self.controller = controller - self.sourceNode = sourceNode + self.sourceView = sourceView } func transitionInfo() -> ContextControllerReferenceViewInfo? { - return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds) + return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds) } } @@ -96,48 +99,6 @@ private final class SortHeaderButton: HighlightableButtonNode { } } -private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool { - if listNode.scroller.isDragging { - return false - } - if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 1.0 { - let offset: CGFloat - if searchNode.expansionProgress < 0.6 { - offset = navigationBarSearchContentHeight - } else { - offset = 0.0 - } - let _ = listNode.scrollToOffsetFromTop(offset, animated: true) - return true - } else if searchNode.expansionProgress == 1.0 { - var sortItemNode: ListViewItemNode? - var nextItemNode: ListViewItemNode? - - listNode.forEachItemNode({ itemNode in - if sortItemNode == nil, let itemNode = itemNode as? ContactListActionItemNode { - sortItemNode = itemNode - } else if sortItemNode != nil && nextItemNode == nil { - nextItemNode = itemNode as? ListViewItemNode - } - }) - - if false, let sortItemNode = sortItemNode { - let itemFrame = sortItemNode.apparentFrame - if itemFrame.contains(CGPoint(x: 0.0, y: listNode.insets.top)) { - var scrollToItem: ListViewScrollToItem? - if itemFrame.minY + itemFrame.height * 0.6 < listNode.insets.top { - scrollToItem = ListViewScrollToItem(index: 0, position: .top(-76.0), animated: true, curve: .Default(duration: 0.3), directionHint: .Up) - } else { - scrollToItem = ListViewScrollToItem(index: 0, position: .top(0), animated: true, curve: .Default(duration: 0.3), directionHint: .Up) - } - listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: scrollToItem, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) - return true - } - } - } - return false -} - public class ContactsController: ViewController { private let context: AccountContext @@ -159,8 +120,6 @@ public class ContactsController: ViewController { private let sortOrderPromise = Promise() private let isInVoiceOver = ValuePromise(false) - private var searchContentNode: NavigationBarSearchContentNode? - public var switchToChatsController: (() -> Void)? public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { @@ -178,7 +137,8 @@ public class ContactsController: ViewController { self.sortButton = SortHeaderButton(presentationData: self.presentationData) - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + //super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: nil) self.tabBarItemContextActionType = .always @@ -209,9 +169,6 @@ public class ContactsController: ViewController { self.scrollToTop = { [weak self] in if let strongSelf = self { - if let searchContentNode = strongSelf.searchContentNode { - searchContentNode.updateExpansionProgress(1.0, animated: true) - } strongSelf.contactsNode.scrollToTop() } } @@ -264,12 +221,6 @@ public class ContactsController: ViewController { }) } - self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search, activate: { [weak self] in - self?.contactsNode.contactListNode.listNode.cancelTracking() - self?.activateSearch() - }) - self.navigationBar?.setContentNode(self.searchContentNode, animated: false) - self.sortButton.addTarget(self, action: #selector(self.sortPressed), forControlEvents: .touchUpInside) } @@ -286,7 +237,7 @@ public class ContactsController: ViewController { self.sortButton.update(theme: self.presentationData.theme, strings: self.presentationData.strings) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) - self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) + self.title = self.presentationData.strings.Contacts_Title self.tabBarItem.title = self.presentationData.strings.Contacts_Title if !self.presentationData.reduceMotion { @@ -313,7 +264,15 @@ public class ContactsController: ViewController { self.displayNode = ContactsControllerNode(context: self.context, sortOrder: sortOrderSignal |> distinctUntilChanged, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) }, controller: self) - self._ready.set(self.contactsNode.contactListNode.ready) + self._ready.set(combineLatest(queue: .mainQueue(), + self.contactsNode.contactListNode.ready, + self.contactsNode.storiesReady.get() + ) + |> filter { a, b in + return a && b + } + |> take(1) + |> map { _ -> Bool in true }) self.contactsNode.navigationBar = self.navigationBar @@ -505,26 +464,8 @@ public class ContactsController: ViewController { } } - self.contactsNode.contactListNode.contentOffsetChanged = { [weak self] offset in - if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode, let validLayout = strongSelf.validLayout { - var offset = offset - if validLayout.inVoiceOver { - offset = .known(0.0) - } - searchContentNode.updateListVisibleContentOffset(offset) - } - } - - self.contactsNode.contactListNode.contentScrollingEnded = { [weak self] listView in - if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode { - return fixListNodeScrolling(listView, searchNode: searchContentNode) - } else { - return false - } - } - self.sortButton.contextAction = { [weak self] sourceNode, gesture in - self?.presentSortMenu(sourceNode: sourceNode, gesture: gesture) + self?.presentSortMenu(sourceView: sourceNode.view, gesture: gesture) } self.displayNodeDidLoad() @@ -533,6 +474,7 @@ public class ContactsController: ViewController { override public func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + self.contactsNode.didAppear = true self.contactsNode.contactListNode.enableUpdates = true } @@ -542,6 +484,22 @@ public class ContactsController: ViewController { self.contactsNode.contactListNode.enableUpdates = false } + private func searchContentNode() -> NavigationBarSearchContentNode? { + if let navigationBarView = self.contactsNode.navigationBarView.view as? ChatListNavigationBar.View { + return navigationBarView.searchContentNode + } + return nil + } + + private func chatListHeaderView() -> ChatListHeaderComponent.View? { + if let navigationBarView = self.contactsNode.navigationBarView.view as? ChatListNavigationBar.View { + if let componentView = navigationBarView.headerContent.view as? ChatListHeaderComponent.View { + return componentView + } + } + return nil + } + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) @@ -550,6 +508,124 @@ public class ContactsController: ViewController { self.validLayout = layout self.contactsNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, actualNavigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) + + if let componentView = self.chatListHeaderView(), componentView.storyPeerAction == nil { + componentView.storyPeerAction = { [weak self] peer in + guard let self else { + return + } + + let storyContent = StoryContentContextImpl(context: self.context, includeHidden: false, focusedPeerId: peer?.id) + let _ = (storyContent.state + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] storyContentState in + guard let self else { + return + } + + if let peer, peer.id == self.context.account.peerId, storyContentState.slice == nil { + return + } + + var transitionIn: StoryContainerScreen.TransitionIn? + if let peer, let componentView = self.chatListHeaderView() { + if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: peer.id) { + transitionIn = StoryContainerScreen.TransitionIn( + sourceView: transitionView, + sourceRect: transitionView.bounds, + sourceCornerRadius: transitionView.bounds.height * 0.5 + ) + } + } + + let storyContainerScreen = StoryContainerScreen( + context: self.context, + content: storyContent, + transitionIn: transitionIn, + transitionOut: { [weak self] peerId, _ in + guard let self else { + return nil + } + + if let componentView = self.chatListHeaderView() { + if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: peerId) { + return StoryContainerScreen.TransitionOut( + destinationView: transitionView, + destinationRect: transitionView.bounds, + destinationCornerRadius: transitionView.bounds.height * 0.5, + destinationIsAvatar: true, + completed: {} + ) + } + } + + return nil + } + ) + self.push(storyContainerScreen) + }) + } + + componentView.storyContextPeerAction = { [weak self] sourceNode, gesture, peer in + guard let self else { + return + } + + var items: [ContextMenuItem] = [] + + //TODO:localize + if peer.id == self.context.account.peerId { + } else { + items.append(.action(ContextMenuActionItem(text: "View Profile", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, _ in + c.dismiss(completion: { + guard let self else { + return + } + + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id) + ) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self else { + return + } + guard let peer = peer, let controller = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) else { + return + } + (self.navigationController as? NavigationController)?.pushViewController(controller) + }) + }) + }))) + /*items.append(.action(ContextMenuActionItem(text: "Mute", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unmute"), color: theme.contextMenu.primaryColor) + }, action: { _, f in + f(.default) + })))*/ + + if case let .user(user) = peer, let storiesHidden = user.storiesHidden, storiesHidden { + items.append(.action(ContextMenuActionItem(text: "Unarchive", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unarchive"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let self else { + return + } + self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: false) + }))) + } + } + + if items.isEmpty { + return + } + + let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) + } + } } @objc private func sortPressed() { @@ -557,24 +633,20 @@ public class ContactsController: ViewController { } private func activateSearch() { - if self.displayNavigationBar { - if let searchContentNode = self.searchContentNode { - self.contactsNode.activateSearch(placeholderNode: searchContentNode.placeholderNode) - } - self.setDisplayNavigationBar(false, transition: .animated(duration: 0.5, curve: .spring)) + if let searchContentNode = self.searchContentNode() { + self.contactsNode.activateSearch(placeholderNode: searchContentNode.placeholderNode) } + self.requestLayout(transition: .animated(duration: 0.5, curve: .spring)) } private func deactivateSearch(animated: Bool) { - if !self.displayNavigationBar { - self.setDisplayNavigationBar(true, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate) - if let searchContentNode = self.searchContentNode { - self.contactsNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated) - } + if let searchContentNode = self.searchContentNode() { + self.contactsNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated) + self.requestLayout(transition: .animated(duration: 0.5, curve: .spring)) } } - private func presentSortMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) { + func presentSortMenu(sourceView: UIView, gesture: ContextGesture?) { let updateSortOrder: (ContactsSortOrder) -> Void = { [weak self] sortOrder in if let strongSelf = self { strongSelf.sortOrderPromise.set(.single(sortOrder)) @@ -609,7 +681,7 @@ public class ContactsController: ViewController { }))) return items } - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceNode: self.sortButton.referenceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) self.presentInGlobalOverlay(contextController) } @@ -708,3 +780,26 @@ private final class ContactsTabBarContextExtractedContentSource: ContextExtracte return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) } } + +private final class ChatListHeaderBarContextExtractedContentSource: ContextExtractedContentSource { + let keepInPlace: Bool + let ignoreContentTouches: Bool = true + let blurBackground: Bool = true + + private let controller: ViewController + private let sourceNode: ContextExtractedContentContainingNode + + init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode, keepInPlace: Bool) { + self.controller = controller + self.sourceNode = sourceNode + self.keepInPlace = keepInPlace + } + + func takeView() -> ContextControllerTakeViewInfo? { + return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index c8c9914257..145357d2de 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -12,6 +12,9 @@ import SearchBarNode import SearchUI import AppBundle import ContextUI +import ChatListHeaderComponent +import ChatListTitleView +import ComponentFlow private final class ContextControllerContentSourceImpl: ContextControllerContentSource { let controller: ViewController @@ -46,10 +49,13 @@ final class ContactsControllerNode: ASDisplayNode { private let context: AccountContext private(set) var searchDisplayController: SearchDisplayController? + private var isSearchDisplayControllerActive: Bool = false + private var storiesUnlocked: Bool = false private var containerLayout: (ContainerViewLayout, CGFloat)? var navigationBar: NavigationBar? + let navigationBarView = ComponentView() var requestDeactivateSearch: (() -> Void)? var requestOpenPeerFromSearch: ((ContactListPeer) -> Void)? @@ -64,6 +70,18 @@ final class ContactsControllerNode: ASDisplayNode { weak var controller: ContactsController? + private var initialScrollingOffset: CGFloat? + private var isSettingUpContentOffset: Bool = false + private var didSetupContentOffset: Bool = false + private var contentOffset: ListViewVisibleContentOffset? + private var ignoreStoryInsetAdjustment: Bool = false + var didAppear: Bool = false + + private(set) var storySubscriptions: EngineStorySubscriptions? + private var storySubscriptionsDisposable: Disposable? + + let storiesReady = Promise() + init(context: AccountContext, sortOrder: Signal, present: @escaping (ViewController, Any?) -> Void, controller: ContactsController) { self.context = context self.controller = controller @@ -139,10 +157,127 @@ final class ContactsControllerNode: ASDisplayNode { contextAction = { [weak self] peer, node, gesture, location in self?.contextAction(peer: peer, node: node, gesture: gesture, location: location) } + + self.contactListNode.contentOffsetChanged = { [weak self] offset in + guard let self else { + return + } + if self.isSettingUpContentOffset { + return + } + + if !self.didSetupContentOffset, let initialScrollingOffset = self.initialScrollingOffset { + self.initialScrollingOffset = nil + self.didSetupContentOffset = true + self.isSettingUpContentOffset = true + + let _ = self.contactListNode.listNode.scrollToOffsetFromTop(initialScrollingOffset, animated: false) + + let offset = self.contactListNode.listNode.visibleContentOffset() + self.contentOffset = offset + self.contentOffsetChanged(offset: offset) + + self.isSettingUpContentOffset = false + return + } + self.contentOffset = offset + self.contentOffsetChanged(offset: offset) + + if self.contactListNode.listNode.isTracking { + if case let .known(value) = offset { + if !self.storiesUnlocked { + if value < -40.0 { + self.storiesUnlocked = true + DispatchQueue.main.async { [weak self] in + guard let self else { + return + } + + HapticFeedback().impact() + + self.contactListNode.ignoreStoryInsetAdjustment = true + self.contactListNode.listNode.allowInsetFixWhileTracking = true + self.onStoriesLockedUpdated(isLocked: true) + self.contactListNode.ignoreStoryInsetAdjustment = false + self.contactListNode.listNode.allowInsetFixWhileTracking = false + } + } + } + } + } else if self.storiesUnlocked { + switch offset { + case let .known(value): + if value >= ChatListNavigationBar.storiesScrollHeight { + self.storiesUnlocked = false + + DispatchQueue.main.async { [weak self] in + self?.onStoriesLockedUpdated(isLocked: false) + } + } + default: + break + } + } + } + + self.contactListNode.contentScrollingEnded = { [weak self] listView in + guard let self else { + return false + } + return self.contentScrollingEnded(listView: listView) + } + + self.storySubscriptionsDisposable = (self.context.engine.messages.storySubscriptions(includeHidden: true) + |> deliverOnMainQueue).start(next: { [weak self] storySubscriptions in + guard let self else { + return + } + + var wasEmpty = true + if let storySubscriptions = self.storySubscriptions, !storySubscriptions.items.isEmpty { + wasEmpty = false + } + self.storySubscriptions = storySubscriptions + let isEmpty = storySubscriptions.items.isEmpty + + let transition: ContainedViewLayoutTransition + if self.didAppear { + transition = .animated(duration: 0.4, curve: .spring) + } else { + transition = .immediate + } + + let _ = wasEmpty + let _ = isEmpty + + //self.chatListDisplayNode.temporaryContentOffsetChangeTransition = transition + self.controller?.requestLayout(transition: transition) + //self.chatListDisplayNode.temporaryContentOffsetChangeTransition = nil + + /*self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in + var chatListState = chatListState + + var peersWithNewStories = Set() + for item in storySubscriptions.items { + if item.peer.id == self.context.account.peerId { + continue + } + if item.hasUnseen { + peersWithNewStories.insert(item.peer.id) + } + } + chatListState.peersWithNewStories = peersWithNewStories + + return chatListState + }*/ + + self.storiesReady.set(.single(true)) + }) } deinit { self.presentationDataDisposable?.dispose() + self.storySubscriptionsDisposable?.dispose() } private func updateThemeAndStrings() { @@ -158,22 +293,180 @@ final class ContactsControllerNode: ASDisplayNode { } } + private func onStoriesLockedUpdated(isLocked: Bool) { + self.controller?.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + } + + private func contentOffsetChanged(offset: ListViewVisibleContentOffset) { + self.updateNavigationScrolling(transition: .immediate) + } + + private func contentScrollingEnded(listView: ListView) -> Bool { + if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View { + if let clippedScrollOffset = navigationBarComponentView.clippedScrollOffset { + if navigationBarComponentView.effectiveStoriesInsetHeight > 0.0 { + if clippedScrollOffset > 0.0 && clippedScrollOffset < navigationBarComponentView.effectiveStoriesInsetHeight { + if clippedScrollOffset < navigationBarComponentView.effectiveStoriesInsetHeight * 0.5 { + let _ = listView.scrollToOffsetFromTop(0.0, animated: true) + } else { + let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight, animated: true) + } + return true + } else { + let searchScrollOffset = clippedScrollOffset - navigationBarComponentView.effectiveStoriesInsetHeight + if searchScrollOffset > 0.0 && searchScrollOffset < ChatListNavigationBar.searchScrollHeight { + if searchScrollOffset < ChatListNavigationBar.searchScrollHeight * 0.5 { + let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight, animated: true) + } else { + let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight + ChatListNavigationBar.searchScrollHeight, animated: true) + } + return true + } + } + } else { + if clippedScrollOffset > 0.0 && clippedScrollOffset < ChatListNavigationBar.searchScrollHeight { + if clippedScrollOffset < ChatListNavigationBar.searchScrollHeight * 0.5 { + let _ = listView.scrollToOffsetFromTop(0.0, animated: true) + } else { + let _ = listView.scrollToOffsetFromTop(ChatListNavigationBar.searchScrollHeight, animated: true) + } + return true + } + } + } + } + + return false + } + + private func updateNavigationBar(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> (navigationHeight: CGFloat, storiesInset: CGFloat) { + let tabsNode: ASDisplayNode? = nil + let tabsNodeIsSearch = false + + //TODO:localize + let primaryContent = ChatListHeaderComponent.Content( + title: "Contacts", + navigationBackTitle: nil, + titleComponent: nil, + chatListTitle: NetworkStatusTitle(text: "Contacts", activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: nil), + leftButton: AnyComponentWithIdentity(id: "sort", component: AnyComponent(NavigationButtonComponent( + content: .text(title: self.presentationData.strings.Contacts_Sort, isBold: false), + pressed: { [weak self] sourceView in + guard let self else { + return + } + + self.controller?.presentSortMenu(sourceView: sourceView, gesture: nil) + } + ))), + rightButtons: [AnyComponentWithIdentity(id: "add", component: AnyComponent(NavigationButtonComponent( + content: .icon(imageName: "Chat List/AddIcon"), + pressed: { [weak self] _ in + guard let self else { + return + } + self.controller?.addPressed() + } + )))], + backTitle: nil, + backPressed: nil + ) + + let navigationBarSize = self.navigationBarView.update( + transition: Transition(transition), + component: AnyComponent(ChatListNavigationBar( + context: self.context, + theme: self.presentationData.theme, + strings: self.presentationData.strings, + statusBarHeight: layout.statusBarHeight ?? 0.0, + sideInset: layout.safeInsets.left, + isSearchActive: self.isSearchDisplayControllerActive, + storiesUnlocked: self.storiesUnlocked, + primaryContent: primaryContent, + secondaryContent: nil, + secondaryTransition: 0.0, + storySubscriptions: self.storySubscriptions, + storiesIncludeHidden: true, + uploadProgress: nil, + tabsNode: tabsNode, + tabsNodeIsSearch: tabsNodeIsSearch, + activateSearch: { [weak self] searchContentNode in + guard let self else { + return + } + + self.contactListNode.activateSearch?() + }, + openStatusSetup: { _ in + } + )), + environment: {}, + containerSize: layout.size + ) + if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View { + navigationBarComponentView.deferScrollApplication = true + + if navigationBarComponentView.superview == nil { + self.view.addSubview(navigationBarComponentView) + } + transition.updateFrame(view: navigationBarComponentView, frame: CGRect(origin: CGPoint(), size: navigationBarSize)) + + return (navigationBarSize.height, navigationBarComponentView.effectiveStoriesInsetHeight) + } else { + return (0.0, 0.0) + } + } + + private func getEffectiveNavigationScrollingOffset() -> CGFloat { + let mainOffset: CGFloat + if let contentOffset = self.contentOffset, case let .known(value) = contentOffset { + mainOffset = value + } else { + mainOffset = 1000.0 + } + + return mainOffset + } + + private func updateNavigationScrolling(transition: ContainedViewLayoutTransition) { + var offset = self.getEffectiveNavigationScrollingOffset() + if self.isSearchDisplayControllerActive { + offset = 0.0 + } + + if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View { + navigationBarComponentView.applyScroll(offset: offset, transition: Transition(transition)) + } + } + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, actualNavigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { self.containerLayout = (layout, navigationBarHeight) + let navigationBarLayout = self.updateNavigationBar(layout: layout, transition: transition) + self.initialScrollingOffset = 0.0//ChatListNavigationBar.searchScrollHeight + navigationBarLayout.storiesInset + var insets = layout.insets(options: [.input]) - insets.top += navigationBarHeight + insets.top += navigationBarLayout.navigationHeight var headerInsets = layout.insets(options: [.input]) - headerInsets.top += actualNavigationBarHeight + headerInsets.top = navigationBarLayout.navigationHeight - navigationBarLayout.storiesInset - ChatListNavigationBar.searchScrollHeight + + let innerLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver) if let searchDisplayController = self.searchDisplayController { - searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) + searchDisplayController.containerLayoutUpdated(innerLayout, navigationBarHeight: navigationBarLayout.navigationHeight, transition: transition) } - self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition) + self.contactListNode.containerLayoutUpdated(innerLayout, headerInsets: headerInsets, storiesInset: navigationBarLayout.storiesInset, transition: transition) self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size) + + self.updateNavigationScrolling(transition: transition) + + if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View { + navigationBarComponentView.deferScrollApplication = false + navigationBarComponentView.applyCurrentScroll(transition: Transition(transition)) + } } private func contextAction(peer: EnginePeer, node: ASDisplayNode?, gesture: ContextGesture?, location: CGPoint?) { @@ -187,11 +480,14 @@ final class ContactsControllerNode: ASDisplayNode { } func activateSearch(placeholderNode: SearchBarPlaceholderNode) { - guard let (containerLayout, navigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else { + guard let (containerLayout, navigationBarHeight) = self.containerLayout, self.searchDisplayController == nil else { return } - self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: false, categories: [.cloudContacts, .global, .deviceContacts], addContact: { [weak self] phoneNumber in + self.isSearchDisplayControllerActive = true + self.storiesUnlocked = false + + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: false, categories: [.cloudContacts, .global, .deviceContacts], addContact: { [weak self] phoneNumber in if let requestAddContact = self?.requestAddContact { requestAddContact(phoneNumber) } @@ -208,21 +504,29 @@ final class ContactsControllerNode: ASDisplayNode { }) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) - self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in - if let strongSelf = self, let strongPlaceholderNode = placeholderNode { + self.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in + if let strongSelf = self { if isSearchBar { - strongPlaceholderNode.supernode?.insertSubnode(subnode, aboveSubnode: strongPlaceholderNode) + if let navigationBarComponentView = strongSelf.navigationBarView.view as? ChatListNavigationBar.View { + navigationBarComponentView.addSubnode(subnode) + } } else { - strongSelf.insertSubnode(subnode, belowSubnode: navigationBar) + strongSelf.insertSubnode(subnode, aboveSubnode: strongSelf.contactListNode) } } }, placeholder: placeholderNode) } func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) { + self.isSearchDisplayControllerActive = false if let searchDisplayController = self.searchDisplayController { + let previousFrame = placeholderNode.frame + placeholderNode.frame = previousFrame.offsetBy(dx: 0.0, dy: 54.0) + searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated) self.searchDisplayController = nil + + placeholderNode.frame = previousFrame } } } diff --git a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift index 7d84d35f65..089090e508 100644 --- a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift @@ -56,7 +56,7 @@ private enum InviteContactsEntry: Comparable, Identifiable { } else { status = .none } - let peer: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: contact.firstName, lastName: contact.lastName, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let peer: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: contact.firstName, lastName: contact.lastName, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: status, enabled: true, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .contacts, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in interaction.toggleContact(id) }) diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index f15d91de48..81bda451d6 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -1412,7 +1412,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { }, openUrl: { _ in }, openPeer: { _ in }, showAll: false) - let peer = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + let peer = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let controller = LocationViewController(context: self.context, subject: EngineMessage(message), params: controllerParams) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift index c899244df4..c971fa218c 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift @@ -726,7 +726,7 @@ public final class InviteLinkViewController: ViewController { if requestsState.importers.isEmpty && requestsState.isLoadingMore { count = min(4, state.count) loading = true - let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) for i in 0 ..< count { entries.append(.request(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, true)) } @@ -760,7 +760,7 @@ public final class InviteLinkViewController: ViewController { if state.importers.isEmpty && state.isLoadingMore { count = min(4, state.count) loading = true - let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) for i in 0 ..< count { entries.append(.importer(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, false, true)) } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift index 072f0bb57a..023e4552da 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift @@ -855,7 +855,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)) var peers = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) let previewText = groupLayouts.count > 1 ? presentationData.strings.Attachment_MessagesPreview : presentationData.strings.Attachment_MessagePreview diff --git a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift index e310908f96..a0f81026d9 100644 --- a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift +++ b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift @@ -663,7 +663,7 @@ private func deviceContactInfoEntries(account: Account, engine: TelegramEngine, firstName = presentationData.strings.Message_Contact } - entries.append(.info(entries.count, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: peer ?? EnginePeer.user(TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: firstName, lastName: isOrganization ? nil : personName.1, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])), state: ItemListAvatarAndNameInfoItemState(editingName: editingName, updatingName: nil), job: isOrganization ? nil : jobSummary, isPlain: !isShare, hiddenAvatar: hiddenAvatar)) + entries.append(.info(entries.count, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: peer ?? EnginePeer.user(TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: firstName, lastName: isOrganization ? nil : personName.1, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)), state: ItemListAvatarAndNameInfoItemState(editingName: editingName, updatingName: nil), job: isOrganization ? nil : jobSummary, isPlain: !isShare, hiddenAvatar: hiddenAvatar)) if !selecting { if let _ = peer { diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index add47c8e2b..8fb01d1a7b 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1256,24 +1256,29 @@ public final class Transaction { return self.postbox!.removeChatHidden(peerId: peerId, index: index) } - public func getAllStorySubscriptions() -> (state: CodableEntry?, peerIds: [PeerId]) { + public func getAllStorySubscriptions(key: PostboxStorySubscriptionsKey) -> (state: CodableEntry?, peerIds: [PeerId]) { assert(!self.disposed) - return self.postbox!.getAllStorySubscriptions() + return self.postbox!.getAllStorySubscriptions(key: key) } - public func replaceAllStorySubscriptions(state: CodableEntry?, peerIds: [PeerId]) { + public func storySubscriptionsContains(key: PostboxStorySubscriptionsKey, peerId: PeerId) -> Bool { assert(!self.disposed) - self.postbox!.replaceAllStorySubscriptions(state: state, peerIds: peerIds) + return self.postbox!.storySubscriptionsContains(key: key, peerId: peerId) } - public func getSubscriptionsStoriesState() -> CodableEntry? { + public func replaceAllStorySubscriptions(key: PostboxStorySubscriptionsKey, state: CodableEntry?, peerIds: [PeerId]) { assert(!self.disposed) - return self.postbox!.getSubscriptionsStoriesState() + self.postbox!.replaceAllStorySubscriptions(key: key, state: state, peerIds: peerIds) } - public func setSubscriptionsStoriesState(state: CodableEntry?) { + public func getSubscriptionsStoriesState(key: PostboxStorySubscriptionsKey) -> CodableEntry? { assert(!self.disposed) - self.postbox!.setSubscriptionsStoriesState(state: state) + return self.postbox!.getSubscriptionsStoriesState(key: key) + } + + public func setSubscriptionsStoriesState(key: PostboxStorySubscriptionsKey, state: CodableEntry?) { + assert(!self.disposed) + self.postbox!.setSubscriptionsStoriesState(key: key, state: state) } public func getLocalStoryState() -> CodableEntry? { @@ -1767,7 +1772,7 @@ final class PostboxImpl { self.deviceContactImportInfoTable = DeviceContactImportInfoTable(valueBox: self.valueBox, table: DeviceContactImportInfoTable.tableSpec(54), useCaches: useCaches) self.groupMessageStatsTable = GroupMessageStatsTable(valueBox: self.valueBox, table: GroupMessageStatsTable.tableSpec(58), useCaches: useCaches) self.storyStatesTable = StoryStatesTable(valueBox: self.valueBox, table: StoryStatesTable.tableSpec(65), useCaches: useCaches) - self.storySubscriptionsTable = StorySubscriptionsTable(valueBox: self.valueBox, table: StorySubscriptionsTable.tableSpec(66), useCaches: useCaches) + self.storySubscriptionsTable = StorySubscriptionsTable(valueBox: self.valueBox, table: StorySubscriptionsTable.tableSpec(78), useCaches: useCaches) self.storyItemsTable = StoryItemsTable(valueBox: self.valueBox, table: StoryItemsTable.tableSpec(69), useCaches: useCaches) self.storyTable = StoryTable(valueBox: self.valueBox, table: StoryTable.tableSpec(70), useCaches: useCaches) @@ -2179,28 +2184,32 @@ final class PostboxImpl { self.synchronizeGroupMessageStatsTable.set(groupId: groupId, namespace: namespace, needsValidation: false, operations: &self.currentUpdatedGroupSummarySynchronizeOperations) } - fileprivate func getAllStorySubscriptions() -> (state: CodableEntry?, peerIds: [PeerId]) { + fileprivate func getAllStorySubscriptions(key: PostboxStorySubscriptionsKey) -> (state: CodableEntry?, peerIds: [PeerId]) { return ( - self.storyStatesTable.get(key: .subscriptions), - self.storySubscriptionsTable.getAll() + self.storyStatesTable.get(key: .subscriptions(key)), + self.storySubscriptionsTable.getAll(subscriptionsKey: key) ) } - fileprivate func replaceAllStorySubscriptions(state: CodableEntry?, peerIds: [PeerId]) { - self.storyStatesTable.set(key: .subscriptions, value: state, events: &self.currentStoryStatesEvents) - self.storySubscriptionsTable.replaceAll(peerIds: peerIds, events: &self.currentStorySubscriptionsEvents) + fileprivate func storySubscriptionsContains(key: PostboxStorySubscriptionsKey, peerId: PeerId) -> Bool { + return self.storySubscriptionsTable.contains(subscriptionsKey: key, peerId: peerId) + } + + fileprivate func replaceAllStorySubscriptions(key: PostboxStorySubscriptionsKey, state: CodableEntry?, peerIds: [PeerId]) { + self.storyStatesTable.set(key: .subscriptions(key), value: state, events: &self.currentStoryStatesEvents) + self.storySubscriptionsTable.replaceAll(subscriptionsKey: key, peerIds: peerIds, events: &self.currentStorySubscriptionsEvents) } fileprivate func getLocalStoryState() -> CodableEntry? { return self.storyStatesTable.get(key: .local) } - fileprivate func getSubscriptionsStoriesState() -> CodableEntry? { - return self.storyStatesTable.get(key: .subscriptions) + fileprivate func getSubscriptionsStoriesState(key: PostboxStorySubscriptionsKey) -> CodableEntry? { + return self.storyStatesTable.get(key: .subscriptions(key)) } - fileprivate func setSubscriptionsStoriesState(state: CodableEntry?) { - self.storyStatesTable.set(key: .subscriptions, value: state, events: &self.currentStoryStatesEvents) + fileprivate func setSubscriptionsStoriesState(key: PostboxStorySubscriptionsKey, state: CodableEntry?) { + self.storyStatesTable.set(key: .subscriptions(key), value: state, events: &self.currentStoryStatesEvents) } fileprivate func setLocalStoryState(state: CodableEntry?) { diff --git a/submodules/Postbox/Sources/StoryStatesTable.swift b/submodules/Postbox/Sources/StoryStatesTable.swift index b34f2ddfab..4f2307ec80 100644 --- a/submodules/Postbox/Sources/StoryStatesTable.swift +++ b/submodules/Postbox/Sources/StoryStatesTable.swift @@ -7,15 +7,21 @@ final class StoryStatesTable: Table { enum Key: Hashable { case local - case subscriptions + case subscriptions(PostboxStorySubscriptionsKey) case peer(PeerId) - init(key: ValueBoxKey) { + init?(key: ValueBoxKey) { switch key.getUInt8(0) { case 0: self = .local case 1: - self = .subscriptions + if key.length != 1 + 4 { + return nil + } + guard let subscriptionsKey = PostboxStorySubscriptionsKey(rawValue: key.getInt32(1)) else { + return nil + } + self = .subscriptions(subscriptionsKey) case 2: self = .peer(PeerId(key.getInt64(1))) default: @@ -30,9 +36,10 @@ final class StoryStatesTable: Table { let key = ValueBoxKey(length: 1) key.setUInt8(0, value: 0) return key - case .subscriptions: - let key = ValueBoxKey(length: 1) + case let .subscriptions(subscriptionsKey): + let key = ValueBoxKey(length: 1 + 4) key.setUInt8(0, value: 1) + key.setInt32(1, value: subscriptionsKey.rawValue) return key case let .peer(peerId): let key = ValueBoxKey(length: 1 + 8) diff --git a/submodules/Postbox/Sources/StoryStatesView.swift b/submodules/Postbox/Sources/StoryStatesView.swift index 6969ba8d37..bb1f192542 100644 --- a/submodules/Postbox/Sources/StoryStatesView.swift +++ b/submodules/Postbox/Sources/StoryStatesView.swift @@ -2,7 +2,7 @@ import Foundation public enum PostboxStoryStatesKey: Hashable { case local - case subscriptions + case subscriptions(PostboxStorySubscriptionsKey) case peer(PeerId) } @@ -11,8 +11,8 @@ private extension PostboxStoryStatesKey { switch tableKey { case .local: self = .local - case .subscriptions: - self = .subscriptions + case let .subscriptions(key): + self = .subscriptions(key) case let .peer(peerId): self = .peer(peerId) } @@ -22,8 +22,8 @@ private extension PostboxStoryStatesKey { switch self { case .local: return .local - case .subscriptions: - return .subscriptions + case let .subscriptions(key): + return .subscriptions(key) case let .peer(peerId): return .peer(peerId) } diff --git a/submodules/Postbox/Sources/StorySubscriptionsTable.swift b/submodules/Postbox/Sources/StorySubscriptionsTable.swift index 89a98762ab..35255f851e 100644 --- a/submodules/Postbox/Sources/StorySubscriptionsTable.swift +++ b/submodules/Postbox/Sources/StorySubscriptionsTable.swift @@ -2,10 +2,11 @@ import Foundation final class StorySubscriptionsTable: Table { enum Event { - case replaceAll + case replaceAll(key: PostboxStorySubscriptionsKey) } private struct Key: Hashable { + var subscriptionsKey: PostboxStorySubscriptionsKey var peerId: PeerId } @@ -13,20 +14,27 @@ final class StorySubscriptionsTable: Table { return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false) } - private let sharedKey = ValueBoxKey(length: 8) + private let sharedKey = ValueBoxKey(length: 4 + 8) private func key(_ key: Key) -> ValueBoxKey { - self.sharedKey.setInt64(0, value: key.peerId.toInt64()) + self.sharedKey.setInt32(0, value: key.subscriptionsKey.rawValue) + self.sharedKey.setInt64(4, value: key.peerId.toInt64()) return self.sharedKey } - private func getAllKeys() -> [Key] { + private func getAllKeys(subscriptionsKey: PostboxStorySubscriptionsKey) -> [Key] { var result: [Key] = [] self.valueBox.scan(self.table, keys: { key in - let peerId = PeerId(key.getInt64(0)) - - result.append(Key(peerId: peerId)) + if key.length != 4 + 8 { + return true + } + if let readSubscriptionsKey = PostboxStorySubscriptionsKey(rawValue: key.getInt32(0)) { + if readSubscriptionsKey == subscriptionsKey { + let peerId = PeerId(key.getInt64(4)) + result.append(Key(subscriptionsKey: subscriptionsKey, peerId: peerId)) + } + } return true }) @@ -34,12 +42,19 @@ final class StorySubscriptionsTable: Table { return result } - public func getAll() -> [PeerId] { + public func getAll(subscriptionsKey: PostboxStorySubscriptionsKey) -> [PeerId] { var result: [PeerId] = [] self.valueBox.scan(self.table, keys: { key in - let peerId = PeerId(key.getInt64(0)) - result.append(peerId) + if key.length != 4 + 8 { + return true + } + if let readSubscriptionsKey = PostboxStorySubscriptionsKey(rawValue: key.getInt32(0)) { + if readSubscriptionsKey == subscriptionsKey { + let peerId = PeerId(key.getInt64(4)) + result.append(peerId) + } + } return true }) @@ -47,16 +62,24 @@ final class StorySubscriptionsTable: Table { return result } - public func replaceAll(peerIds: [PeerId], events: inout [Event]) { - for key in self.getAllKeys() { + public func contains(subscriptionsKey: PostboxStorySubscriptionsKey, peerId: PeerId) -> Bool { + if let _ = self.valueBox.get(self.table, key: self.key(Key(subscriptionsKey: subscriptionsKey, peerId: peerId))) { + return true + } else { + return false + } + } + + public func replaceAll(subscriptionsKey: PostboxStorySubscriptionsKey, peerIds: [PeerId], events: inout [Event]) { + for key in self.getAllKeys(subscriptionsKey: subscriptionsKey) { self.valueBox.remove(self.table, key: self.key(key), secure: true) } for peerId in peerIds { - self.valueBox.set(self.table, key: self.key(Key(peerId: peerId)), value: MemoryBuffer()) + self.valueBox.set(self.table, key: self.key(Key(subscriptionsKey: subscriptionsKey, peerId: peerId)), value: MemoryBuffer()) } - events.append(.replaceAll) + events.append(.replaceAll(key: subscriptionsKey)) } override func clearMemoryCache() { diff --git a/submodules/Postbox/Sources/StorySubscriptionsView.swift b/submodules/Postbox/Sources/StorySubscriptionsView.swift index 34395d6e55..b6c0b59a7f 100644 --- a/submodules/Postbox/Sources/StorySubscriptionsView.swift +++ b/submodules/Postbox/Sources/StorySubscriptionsView.swift @@ -1,10 +1,17 @@ import Foundation +public enum PostboxStorySubscriptionsKey: Int32 { + case all = 0 + case filtered = 1 +} + final class MutableStorySubscriptionsView: MutablePostboxView { + private let key: PostboxStorySubscriptionsKey var peerIds: [PeerId] - init(postbox: PostboxImpl) { - self.peerIds = postbox.storySubscriptionsTable.getAll() + init(postbox: PostboxImpl, key: PostboxStorySubscriptionsKey) { + self.key = key + self.peerIds = postbox.storySubscriptionsTable.getAll(subscriptionsKey: self.key) } func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { @@ -12,14 +19,16 @@ final class MutableStorySubscriptionsView: MutablePostboxView { if !transaction.storySubscriptionsEvents.isEmpty { loop: for event in transaction.storySubscriptionsEvents { switch event { - case .replaceAll: - let peerIds = postbox.storySubscriptionsTable.getAll() - if self.peerIds != peerIds { - updated = true - self.peerIds = peerIds + case let .replaceAll(key): + if key == self.key { + let peerIds = postbox.storySubscriptionsTable.getAll(subscriptionsKey: self.key) + if self.peerIds != peerIds { + updated = true + self.peerIds = peerIds + } + + break loop } - - break loop } } } @@ -27,7 +36,7 @@ final class MutableStorySubscriptionsView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - let peerIds = postbox.storySubscriptionsTable.getAll() + let peerIds = postbox.storySubscriptionsTable.getAll(subscriptionsKey: self.key) if self.peerIds != peerIds { self.peerIds = peerIds diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index aa604f7fa8..4b5f8230c3 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -40,7 +40,7 @@ public enum PostboxViewKey: Hashable { case peerTimeoutAttributes case messageHistoryThreadIndex(id: PeerId, summaryComponents: ChatListEntrySummaryComponents) case messageHistoryThreadInfo(peerId: PeerId, threadId: Int64) - case storySubscriptions + case storySubscriptions(key: PostboxStorySubscriptionsKey) case storiesState(key: PostboxStoryStatesKey) case storyItems(peerId: PeerId) @@ -137,8 +137,9 @@ public enum PostboxViewKey: Hashable { case let .messageHistoryThreadInfo(peerId, threadId): hasher.combine(peerId) hasher.combine(threadId) - case .storySubscriptions: + case let .storySubscriptions(key): hasher.combine(18) + hasher.combine(key) case let .storiesState(key): hasher.combine(key) case let .storyItems(peerId): @@ -382,8 +383,8 @@ public enum PostboxViewKey: Hashable { } else { return false } - case .storySubscriptions: - if case .storySubscriptions = rhs { + case let .storySubscriptions(key): + if case .storySubscriptions(key) = rhs { return true } else { return false @@ -484,8 +485,8 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost return MutableMessageHistoryThreadIndexView(postbox: postbox, peerId: id, summaryComponents: summaryComponents) case let .messageHistoryThreadInfo(peerId, threadId): return MutableMessageHistoryThreadInfoView(postbox: postbox, peerId: peerId, threadId: threadId) - case .storySubscriptions: - return MutableStorySubscriptionsView(postbox: postbox) + case let .storySubscriptions(key): + return MutableStorySubscriptionsView(postbox: postbox, key: key) case let .storiesState(key): return MutableStoryStatesView(postbox: postbox, key: key) case let .storyItems(peerId): diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index ff84d1232f..ae79240647 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -163,8 +163,8 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index 1040c6bd0e..44b91a3feb 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -145,7 +145,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { var peers = SimpleDictionary() let messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: item.peerName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: item.peerName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName, psaType: nil, flags: []) diff --git a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift index 70b6fe18ee..e3791455ad 100644 --- a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift @@ -274,7 +274,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { var peers = SimpleDictionary() let messages = SimpleDictionary() - peers[userPeerId] = TelegramUser(id: userPeerId, accessHash: nil, firstName: item.strings.Settings_QuickReactionSetup_DemoMessageAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + peers[userPeerId] = TelegramUser(id: userPeerId, accessHash: nil, firstName: item.strings.Settings_QuickReactionSetup_DemoMessageAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) let messageText = item.strings.Settings_QuickReactionSetup_DemoMessageText diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 819b125e7b..2beea7579e 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -302,14 +302,14 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView ) } - let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [])) - let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [])) - let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let timestamp = self.referenceTimestamp @@ -425,8 +425,8 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 5eb1946add..ff1437b1d4 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -935,12 +935,12 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate ) } - let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [])) - let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let timestamp = self.referenceTimestamp @@ -1028,8 +1028,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) var sampleMessages: [Message] = [] diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index e782b435dd..2495ab4d75 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -450,15 +450,15 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let chatListPresentationData = ChatListPresentationData(theme: self.previewTheme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) - let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [])) - let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [])) - let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) - let peer7: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) + let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) + let peer7: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil)) let timestamp = self.referenceTimestamp @@ -579,8 +579,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) var sampleMessages: [Message] = [] diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 72444502bb..2e7d5b3a82 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -155,11 +155,11 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) if let (author, text) = messageItem.reply { - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: author, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: author, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) } - let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, isCentered: false)) } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index c76a281073..f2a602ebe7 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -1485,8 +1485,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() let messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) var topMessageText = "" var bottomMessageText = "" diff --git a/submodules/TelegramCore/Sources/Account/Account.swift b/submodules/TelegramCore/Sources/Account/Account.swift index a108f7ffe7..d8424b9fb0 100644 --- a/submodules/TelegramCore/Sources/Account/Account.swift +++ b/submodules/TelegramCore/Sources/Account/Account.swift @@ -973,7 +973,8 @@ public class Account { let networkStatsContext: NetworkStatsContext - public let storySubscriptionsContext: StorySubscriptionsContext? + public let filteredStorySubscriptionsContext: StorySubscriptionsContext? + public let allStorySubscriptionsContext: StorySubscriptionsContext? public init(accountManager: AccountManager, id: AccountRecordId, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, networkArguments: NetworkInitializationArguments, peerId: PeerId, auxiliaryMethods: AccountAuxiliaryMethods, supplementary: Bool) { self.accountManager = accountManager @@ -993,9 +994,11 @@ public class Account { self.peerInputActivityManager = PeerInputActivityManager() if !supplementary { - self.storySubscriptionsContext = StorySubscriptionsContext(accountPeerId: peerId, postbox: postbox, network: network) + self.filteredStorySubscriptionsContext = StorySubscriptionsContext(accountPeerId: peerId, postbox: postbox, network: network, includesHidden: false) + self.allStorySubscriptionsContext = StorySubscriptionsContext(accountPeerId: peerId, postbox: postbox, network: network, includesHidden: true) } else { - self.storySubscriptionsContext = nil + self.filteredStorySubscriptionsContext = nil + self.allStorySubscriptionsContext = nil } self.callSessionManager = CallSessionManager(postbox: postbox, network: network, maxLayer: networkArguments.voipMaxLayer, versions: networkArguments.voipVersions, addUpdates: { [weak self] updates in diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift index bae9426009..f6893936d2 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift @@ -68,6 +68,11 @@ extension TelegramUser { userFlags.insert(.isCloseFriend) } + var storiesHidden: Bool? + if !isMin { + storiesHidden = (flags2 & (1 << 5)) != 0 + } + var botInfo: BotUserInfo? if (flags & (1 << 14)) != 0 { var botFlags = BotUserInfoFlags() @@ -91,9 +96,9 @@ extension TelegramUser { let restrictionInfo: PeerAccessRestrictionInfo? = restrictionReason.flatMap(PeerAccessRestrictionInfo.init(apiReasons:)) - self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? []) + self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden) case let .userEmpty(id): - self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) } } @@ -174,7 +179,7 @@ extension TelegramUser { accessHash = lhs.accessHash ?? rhsAccessHashValue } - return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: lhs.usernames) + return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: lhs.usernames, storiesHidden: lhs.storiesHidden) } else { return TelegramUser(user: rhs) } @@ -228,7 +233,14 @@ extension TelegramUser { photo = rhs.photo } - return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: photo, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus, usernames: lhs.usernames) + var storiesHidden: Bool? + if let value = rhs.storiesHidden { + storiesHidden = value + } else { + storiesHidden = lhs.storiesHidden + } + + return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: photo, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus, usernames: lhs.usernames, storiesHidden: storiesHidden) } } } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 0056141c20..a29649a712 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1080,7 +1080,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: if updatedState.peers[peerId] == nil { updatedState.updatePeer(peerId, { peer in if peer == nil { - return TelegramUser(id: peerId, accessHash: nil, firstName: "Telegram Notifications", lastName: nil, username: nil, phone: nil, photo: [], botInfo: BotUserInfo(flags: [], inlinePlaceholder: nil), restrictionInfo: nil, flags: [.isVerified], emojiStatus: nil, usernames: []) + return TelegramUser(id: peerId, accessHash: nil, firstName: "Telegram Notifications", lastName: nil, username: nil, phone: nil, photo: [], botInfo: BotUserInfo(flags: [], inlinePlaceholder: nil), restrictionInfo: nil, flags: [.isVerified], emojiStatus: nil, usernames: [], storiesHidden: nil) } else { return peer } @@ -4351,9 +4351,9 @@ func replayFinalState( } } - var subscriptionsOpaqueState: String? - if let state = transaction.getSubscriptionsStoriesState()?.get(Stories.SubscriptionsState.self) { - subscriptionsOpaqueState = state.opaqueState + var filteredSubscriptionsOpaqueState: String? + if let state = transaction.getSubscriptionsStoriesState(key: .filtered)?.get(Stories.SubscriptionsState.self) { + filteredSubscriptionsOpaqueState = state.opaqueState } var appliedMaxReadId: Int32? if let currentState = transaction.getPeerStoryState(peerId: peerId)?.get(Stories.PeerState.self) { @@ -4366,7 +4366,7 @@ func replayFinalState( transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries) transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState( - subscriptionsOpaqueState: subscriptionsOpaqueState, + subscriptionsOpaqueState: filteredSubscriptionsOpaqueState, maxReadId: appliedMaxReadId ?? 0 ))) @@ -4381,12 +4381,12 @@ func replayFinalState( appliedMaxReadId = max(appliedMaxReadId, currentState.maxReadId) } - var subscriptionsOpaqueState: String? - if let state = transaction.getSubscriptionsStoriesState()?.get(Stories.SubscriptionsState.self) { - subscriptionsOpaqueState = state.opaqueState + var filteredSubscriptionsOpaqueState: String? + if let state = transaction.getSubscriptionsStoriesState(key: .filtered)?.get(Stories.SubscriptionsState.self) { + filteredSubscriptionsOpaqueState = state.opaqueState } transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState( - subscriptionsOpaqueState: subscriptionsOpaqueState, + subscriptionsOpaqueState: filteredSubscriptionsOpaqueState, maxReadId: appliedMaxReadId ))) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift index f7815f68d2..6a45326dec 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift @@ -109,6 +109,7 @@ public final class TelegramUser: Peer, Equatable { public let flags: UserInfoFlags public let emojiStatus: PeerEmojiStatus? public let usernames: [TelegramPeerUsername] + public let storiesHidden: Bool? public var nameOrPhone: String { if let firstName = self.firstName { @@ -169,7 +170,7 @@ public final class TelegramUser: Peer, Equatable { } } - public init(id: PeerId, accessHash: TelegramPeerAccessHash?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: [TelegramMediaImageRepresentation], botInfo: BotUserInfo?, restrictionInfo: PeerAccessRestrictionInfo?, flags: UserInfoFlags, emojiStatus: PeerEmojiStatus?, usernames: [TelegramPeerUsername]) { + public init(id: PeerId, accessHash: TelegramPeerAccessHash?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: [TelegramMediaImageRepresentation], botInfo: BotUserInfo?, restrictionInfo: PeerAccessRestrictionInfo?, flags: UserInfoFlags, emojiStatus: PeerEmojiStatus?, usernames: [TelegramPeerUsername], storiesHidden: Bool?) { self.id = id self.accessHash = accessHash self.firstName = firstName @@ -182,6 +183,7 @@ public final class TelegramUser: Peer, Equatable { self.flags = flags self.emojiStatus = emojiStatus self.usernames = usernames + self.storiesHidden = storiesHidden } public init(decoder: PostboxDecoder) { @@ -220,6 +222,7 @@ public final class TelegramUser: Peer, Equatable { self.emojiStatus = decoder.decode(PeerEmojiStatus.self, forKey: "emjs") self.usernames = decoder.decodeObjectArrayForKey("uns") + self.storiesHidden = decoder.decodeOptionalBoolForKey("sth") } public func encode(_ encoder: PostboxEncoder) { @@ -273,6 +276,12 @@ public final class TelegramUser: Peer, Equatable { } encoder.encodeObjectArray(self.usernames, forKey: "uns") + + if let storiesHidden = self.storiesHidden { + encoder.encodeBool(storiesHidden, forKey: "sth") + } else { + encoder.encodeNil(forKey: "sth") + } } public func isEqual(_ other: Peer) -> Bool { @@ -325,35 +334,42 @@ public final class TelegramUser: Peer, Equatable { if lhs.usernames != rhs.usernames { return false } + if lhs.storiesHidden != rhs.storiesHidden { + return false + } return true } public func withUpdatedUsername(_ username: String?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden) } public func withUpdatedUsernames(_ usernames: [TelegramPeerUsername]) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: usernames) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: usernames, storiesHidden: self.storiesHidden) } public func withUpdatedNames(firstName: String?, lastName: String?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: firstName, lastName: lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: firstName, lastName: lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden) } public func withUpdatedPhone(_ phone: String?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden) } public func withUpdatedPhoto(_ representations: [TelegramMediaImageRepresentation]) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: representations, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: representations, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden) } public func withUpdatedEmojiStatus(_ emojiStatus: PeerEmojiStatus?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: emojiStatus, usernames: self.usernames) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden) } public func withUpdatedFlags(_ flags: UserInfoFlags) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: emojiStatus, usernames: self.usernames) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden) + } + + public func withUpdatedStoriesHidden(_ storiesHidden: Bool?) -> TelegramUser { + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: storiesHidden) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index 65e5b75622..1cea3b2f8d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -1290,3 +1290,53 @@ public final class EngineStoryViewListContext { } } } + +func _internal_updatePeerStoriesHidden(account: Account, id: PeerId, isHidden: Bool) -> Signal { + return account.postbox.transaction { transaction -> Api.InputUser? in + guard let peer = transaction.getPeer(id) else { + return nil + } + guard let user = peer as? TelegramUser else { + return nil + } + updatePeers(transaction: transaction, peers: [user.withUpdatedStoriesHidden(isHidden)], update: { _, updated in + return updated + }) + return apiInputUser(peer) + } + |> mapToSignal { inputUser -> Signal in + guard let inputUser = inputUser else { + return .complete() + } + return account.network.request(Api.functions.contacts.toggleStoriesHidden(id: inputUser, hidden: isHidden ? .boolTrue : .boolFalse)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> ignoreValues + } +} + +func _internal_exportStoryLink(account: Account, peerId: EnginePeer.Id, id: Int32) -> Signal { + return account.postbox.transaction { transaction -> Api.InputUser? in + return transaction.getPeer(peerId).flatMap(apiInputUser) + } + |> mapToSignal { inputUser -> Signal in + guard let inputUser = inputUser else { + return .single(nil) + } + return account.network.request(Api.functions.stories.exportStoryLink(userId: inputUser, id: id)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> String? in + guard let result = result else { + return nil + } + switch result { + case let .exportedStoryLink(link): + return link + } + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index 5ccbdb9b56..b542db1a53 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -111,6 +111,7 @@ public final class StorySubscriptionsContext { private let queue: Queue private let postbox: Postbox private let network: Network + private let includesHidden: Bool private var taskState = TaskState() @@ -121,11 +122,12 @@ public final class StorySubscriptionsContext { private let loadMoreDisposable = MetaDisposable() private let refreshTimerDisposable = MetaDisposable() - init(queue: Queue, accountPeerId: PeerId, postbox: Postbox, network: Network) { + init(queue: Queue, accountPeerId: PeerId, postbox: Postbox, network: Network, includesHidden: Bool) { self.accountPeerId = accountPeerId self.queue = queue self.postbox = postbox self.network = network + self.includesHidden = includesHidden self.taskState.isRefreshScheduled = true @@ -148,16 +150,18 @@ public final class StorySubscriptionsContext { return } + let subscriptionsKey: PostboxStorySubscriptionsKey = self.includesHidden ? .all : .filtered + if self.taskState.isRefreshScheduled { self.isLoading = true - self.stateDisposable = (postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .subscriptions)]) + self.stateDisposable = (postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))]) |> take(1) |> deliverOn(self.queue)).start(next: { [weak self] views in guard let `self` = self else { return } - guard let storiesStateView = views.views[PostboxViewKey.storiesState(key: .subscriptions)] as? StoryStatesView else { + guard let storiesStateView = views.views[PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))] as? StoryStatesView else { return } @@ -173,13 +177,13 @@ public final class StorySubscriptionsContext { } else if self.taskState.isLoadMoreScheduled { self.isLoading = true - self.stateDisposable = (postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .subscriptions)]) + self.stateDisposable = (postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))]) |> take(1) |> deliverOn(self.queue)).start(next: { [weak self] views in guard let `self` = self else { return } - guard let storiesStateView = views.views[PostboxViewKey.storiesState(key: .subscriptions)] as? StoryStatesView else { + guard let storiesStateView = views.views[PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))] as? StoryStatesView else { return } @@ -206,6 +210,11 @@ public final class StorySubscriptionsContext { private func loadImpl(isRefresh: Bool, stateMark: OpaqueStateMark) { var flags: Int32 = 0 + + if self.includesHidden { + flags |= 1 << 2 + } + var state: String? switch stateMark { case .empty: @@ -228,6 +237,9 @@ public final class StorySubscriptionsContext { let accountPeerId = self.accountPeerId + let includesHidden = self.includesHidden + let subscriptionsKey: PostboxStorySubscriptionsKey = self.includesHidden ? .all : .filtered + self.loadMoreDisposable.set((self.network.request(Api.functions.stories.getAllStories(flags: flags, state: state)) |> deliverOn(self.queue)).start(next: { [weak self] result in guard let `self` = self else { @@ -238,7 +250,7 @@ public final class StorySubscriptionsContext { switch result { case let .allStoriesNotModified(state): self.loadedStateMark = .value(state) - let (currentStateValue, _) = transaction.getAllStorySubscriptions() + let (currentStateValue, _) = transaction.getAllStorySubscriptions(key: subscriptionsKey) let currentState = currentStateValue.flatMap { $0.get(Stories.SubscriptionsState.self) } var hasMore = false @@ -246,7 +258,7 @@ public final class StorySubscriptionsContext { hasMore = currentState.hasMore } - transaction.setSubscriptionsStoriesState(state: CodableEntry(Stories.SubscriptionsState( + transaction.setSubscriptionsStoriesState(key: subscriptionsKey, state: CodableEntry(Stories.SubscriptionsState( opaqueState: state, refreshId: currentState?.refreshId ?? UInt64.random(in: 0 ... UInt64.max), hasMore: hasMore @@ -263,14 +275,9 @@ public final class StorySubscriptionsContext { peerPresences[telegramUser.id] = user } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) - let hasMore: Bool = (flags & (1 << 0)) != 0 - let (_, currentPeerItems) = transaction.getAllStorySubscriptions() + let (_, currentPeerItems) = transaction.getAllStorySubscriptions(key: subscriptionsKey) var peerEntries: [PeerId] = [] for userStorySet in userStories { @@ -312,18 +319,29 @@ public final class StorySubscriptionsContext { } } - if !isRefresh { + if isRefresh { + if !includesHidden { + if !peerEntries.contains(where: { $0 == accountPeerId }) { + transaction.setStoryItems(peerId: accountPeerId, items: []) + } + } + } else { let leftPeerIds = currentPeerItems.filter({ !peerEntries.contains($0) }) if !leftPeerIds.isEmpty { peerEntries = leftPeerIds + peerEntries } } - transaction.replaceAllStorySubscriptions(state: CodableEntry(Stories.SubscriptionsState( + transaction.replaceAllStorySubscriptions(key: subscriptionsKey, state: CodableEntry(Stories.SubscriptionsState( opaqueState: state, refreshId: UInt64.random(in: 0 ... UInt64.max), hasMore: hasMore )), peerIds: peerEntries) + + updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in + return updated + }) + updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) } } |> deliverOn(self.queue)).start(completed: { [weak self] in @@ -355,10 +373,10 @@ public final class StorySubscriptionsContext { private let queue = Queue(name: "StorySubscriptionsContext") private let impl: QueueLocalObject - init(accountPeerId: PeerId, postbox: Postbox, network: Network) { + init(accountPeerId: PeerId, postbox: Postbox, network: Network, includesHidden: Bool) { let queue = self.queue self.impl = QueueLocalObject(queue: queue, generate: { - Impl(queue: queue, accountPeerId: accountPeerId, postbox: postbox, network: network) + Impl(queue: queue, accountPeerId: accountPeerId, postbox: postbox, network: network, includesHidden: includesHidden) }) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 3a1cf58b3a..d0dfd4fc6e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -592,7 +592,7 @@ public extension TelegramEngine { }).start() } - public func storySubscriptions() -> Signal { + public func storySubscriptions(includeHidden: Bool) -> Signal { let debugTimerSignal: Signal #if DEBUG && false debugTimerSignal = Signal.single(true) @@ -609,21 +609,24 @@ public extension TelegramEngine { debugTimerSignal = .single(true) #endif + let subscriptionsKey: PostboxStorySubscriptionsKey = includeHidden ? .all : .filtered + let basicPeerKey = PostboxViewKey.basicPeer(self.account.peerId) + let storySubscriptionsKey = PostboxViewKey.storySubscriptions(key: subscriptionsKey) return combineLatest(debugTimerSignal |> distinctUntilChanged, self.account.postbox.combinedView(keys: [ basicPeerKey, - PostboxViewKey.storySubscriptions, - PostboxViewKey.storiesState(key: .subscriptions) + storySubscriptionsKey, + PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey)) ])) |> mapToSignal { debugTimer, views -> Signal in guard let basicPeerView = views.views[basicPeerKey] as? BasicPeerView, let accountPeer = basicPeerView.peer else { return .single(EngineStorySubscriptions(accountItem: nil, items: [], hasMoreToken: nil)) } - guard let storySubscriptionsView = views.views[PostboxViewKey.storySubscriptions] as? StorySubscriptionsView else { + guard let storySubscriptionsView = views.views[storySubscriptionsKey] as? StorySubscriptionsView else { return .single(EngineStorySubscriptions(accountItem: nil, items: [], hasMoreToken: nil)) } - guard let storiesStateView = views.views[PostboxViewKey.storiesState(key: .subscriptions)] as? StoryStatesView else { + guard let storiesStateView = views.views[PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))] as? StoryStatesView else { return .single(EngineStorySubscriptions(accountItem: nil, items: [], hasMoreToken: nil)) } @@ -756,21 +759,23 @@ public extension TelegramEngine { } } - public func preloadStorySubscriptions() -> Signal<[EngineMediaResource.Id: StoryPreloadInfo], NoError> { + public func preloadStorySubscriptions(includeHidden: Bool) -> Signal<[EngineMediaResource.Id: StoryPreloadInfo], NoError> { let basicPeerKey = PostboxViewKey.basicPeer(self.account.peerId) + let subscriptionsKey: PostboxStorySubscriptionsKey = includeHidden ? .all : .filtered + let storySubscriptionsKey = PostboxViewKey.storySubscriptions(key: subscriptionsKey) return self.account.postbox.combinedView(keys: [ basicPeerKey, - PostboxViewKey.storySubscriptions, - PostboxViewKey.storiesState(key: .subscriptions) + storySubscriptionsKey, + PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey)) ]) |> mapToSignal { views -> Signal<[EngineMediaResource.Id: StoryPreloadInfo], NoError> in guard let basicPeerView = views.views[basicPeerKey] as? BasicPeerView, let accountPeer = basicPeerView.peer else { return .single([:]) } - guard let storySubscriptionsView = views.views[PostboxViewKey.storySubscriptions] as? StorySubscriptionsView else { + guard let storySubscriptionsView = views.views[storySubscriptionsKey] as? StorySubscriptionsView else { return .single([:]) } - guard let storiesStateView = views.views[PostboxViewKey.storiesState(key: .subscriptions)] as? StoryStatesView else { + guard let storiesStateView = views.views[PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))] as? StoryStatesView else { return .single([:]) } @@ -899,5 +904,9 @@ public extension TelegramEngine { public func storyViewList(id: Int32, views: EngineStoryItem.Views) -> EngineStoryViewListContext { return EngineStoryViewListContext(account: self.account, storyId: id, views: views) } + + public func exportStoryLink(peerId: EnginePeer.Id, id: Int32) -> Signal { + return _internal_exportStoryLink(account: self.account, peerId: peerId, id: id) + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index b92a84d4cb..fde91bc8c3 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -1132,6 +1132,10 @@ public extension TelegramEngine { public func tokenizeSearchString(string: String, transliteration: EngineStringIndexTokenTransliteration) -> [EngineDataBuffer] { return stringIndexTokens(string, transliteration: transliteration) } + + public func updatePeerStoriesHidden(id: PeerId, isHidden: Bool) { + let _ = _internal_updatePeerStoriesHidden(account: self.account, id: id, isHidden: isHidden).start() + } } } diff --git a/submodules/TelegramCore/Sources/UpdatePeers.swift b/submodules/TelegramCore/Sources/UpdatePeers.swift index cb9315719b..c694deb0d8 100644 --- a/submodules/TelegramCore/Sources/UpdatePeers.swift +++ b/submodules/TelegramCore/Sources/UpdatePeers.swift @@ -53,7 +53,22 @@ public func updatePeers(transaction: Transaction, peers: [Peer], update: (Peer?, switch peerId.namespace { case Namespaces.Peer.CloudUser: - break + if let updated = updated as? TelegramUser, let storiesHidden = updated.storiesHidden { + if storiesHidden { + if transaction.storySubscriptionsContains(key: .filtered, peerId: updated.id) { + var (state, peerIds) = transaction.getAllStorySubscriptions(key: .filtered) + peerIds.removeAll(where: { $0 == updated.id }) + transaction.replaceAllStorySubscriptions(key: .filtered, state: state, peerIds: peerIds) + } + } else { + if transaction.storySubscriptionsContains(key: .all, peerId: updated.id) && !transaction.storySubscriptionsContains(key: .filtered, peerId: updated.id) { + var (state, peerIds) = transaction.getAllStorySubscriptions(key: .all) + peerIds.removeAll(where: { $0 == updated.id }) + peerIds.append(updated.id) + transaction.replaceAllStorySubscriptions(key: .filtered, state: state, peerIds: peerIds) + } + } + } case Namespaces.Peer.CloudGroup: if let group = updated as? TelegramGroup { if group.flags.contains(.deactivated) { diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift index 075ebf5671..6e7cce713c 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift @@ -133,7 +133,9 @@ public final class ChatListHeaderComponent: Component { public let secondaryTransition: CGFloat public let networkStatus: HeaderNetworkStatusComponent.Content? public let storySubscriptions: EngineStorySubscriptions? + public let storiesIncludeHidden: Bool public let storiesFraction: CGFloat + public let storiesUnlockedFraction: CGFloat public let uploadProgress: Float? public let context: AccountContext public let theme: PresentationTheme @@ -149,7 +151,9 @@ public final class ChatListHeaderComponent: Component { secondaryTransition: CGFloat, networkStatus: HeaderNetworkStatusComponent.Content?, storySubscriptions: EngineStorySubscriptions?, + storiesIncludeHidden: Bool, storiesFraction: CGFloat, + storiesUnlockedFraction: CGFloat, uploadProgress: Float?, context: AccountContext, theme: PresentationTheme, @@ -164,7 +168,9 @@ public final class ChatListHeaderComponent: Component { self.context = context self.networkStatus = networkStatus self.storySubscriptions = storySubscriptions + self.storiesIncludeHidden = storiesIncludeHidden self.storiesFraction = storiesFraction + self.storiesUnlockedFraction = storiesUnlockedFraction self.uploadProgress = uploadProgress self.theme = theme self.strings = strings @@ -191,9 +197,15 @@ public final class ChatListHeaderComponent: Component { if lhs.storySubscriptions != rhs.storySubscriptions { return false } + if lhs.storiesIncludeHidden != rhs.storiesIncludeHidden { + return false + } if lhs.storiesFraction != rhs.storiesFraction { return false } + if lhs.storiesUnlockedFraction != rhs.storiesUnlockedFraction { + return false + } if lhs.uploadProgress != rhs.uploadProgress { return false } @@ -799,12 +811,6 @@ public final class ChatListHeaderComponent: Component { var storyListTransition = transition if let storySubscriptions = component.storySubscriptions { - var storyOffsetFraction: CGFloat = 1.0 - storyOffsetFraction = component.storiesFraction - let _ = storyOffsetFraction - - //self.storyOffsetFraction = storyOffsetFraction - let storyPeerList: ComponentView if let current = self.storyPeerList { storyPeerList = current @@ -821,8 +827,10 @@ public final class ChatListHeaderComponent: Component { context: component.context, theme: component.theme, strings: component.strings, + includesHidden: component.storiesIncludeHidden, storySubscriptions: storySubscriptions, collapseFraction: 1.0 - component.storiesFraction, + unlockedFraction: 1.0 - component.storiesUnlockedFraction, uploadProgress: component.uploadProgress, peerAction: { [weak self] peer in guard let self else { @@ -971,8 +979,8 @@ public final class ChatListHeaderComponent: Component { self.addSubview(storyPeerListComponentView) } - let storyPeerListMinOffset: CGFloat = -8.0 - let storyPeerListMaxOffset: CGFloat = availableSize.height + 2.0 + let storyPeerListMinOffset: CGFloat = -7.0 + let storyPeerListMaxOffset: CGFloat = availableSize.height + 8.0 let storyPeerListPosition: CGFloat = storyPeerListMinOffset * (1.0 - component.storiesFraction) + storyPeerListMaxOffset * component.storiesFraction diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift index 80a5b41ec2..7728a9bbe7 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift @@ -21,6 +21,7 @@ public final class ChatListNavigationBar: Component { public let secondaryContent: ChatListHeaderComponent.Content? public let secondaryTransition: CGFloat public let storySubscriptions: EngineStorySubscriptions? + public let storiesIncludeHidden: Bool public let uploadProgress: Float? public let tabsNode: ASDisplayNode? public let tabsNodeIsSearch: Bool @@ -39,6 +40,7 @@ public final class ChatListNavigationBar: Component { secondaryContent: ChatListHeaderComponent.Content?, secondaryTransition: CGFloat, storySubscriptions: EngineStorySubscriptions?, + storiesIncludeHidden: Bool, uploadProgress: Float?, tabsNode: ASDisplayNode?, tabsNodeIsSearch: Bool, @@ -56,6 +58,7 @@ public final class ChatListNavigationBar: Component { self.secondaryContent = secondaryContent self.secondaryTransition = secondaryTransition self.storySubscriptions = storySubscriptions + self.storiesIncludeHidden = storiesIncludeHidden self.uploadProgress = uploadProgress self.tabsNode = tabsNode self.tabsNodeIsSearch = tabsNodeIsSearch @@ -97,6 +100,9 @@ public final class ChatListNavigationBar: Component { if lhs.storySubscriptions != rhs.storySubscriptions { return false } + if lhs.storiesIncludeHidden != rhs.storiesIncludeHidden { + return false + } if lhs.uploadProgress != rhs.uploadProgress { return false } @@ -116,6 +122,9 @@ public final class ChatListNavigationBar: Component { self.size = size } } + + public static let searchScrollHeight: CGFloat = 52.0 + public static let storiesScrollHeight: CGFloat = 94.0 public final class View: UIView { private let backgroundView: BlurredBackgroundView @@ -142,7 +151,9 @@ public final class ChatListNavigationBar: Component { private var applyScrollFractionAnimator: DisplayLinkAnimator? private var applyScrollFraction: CGFloat = 1.0 + private var applyScrollUnlockedFraction: CGFloat = 1.0 private var applyScrollStartFraction: CGFloat = 0.0 + private var storiesOffsetFraction: CGFloat = 0.0 private var tabsNode: ASDisplayNode? private var tabsNodeIsSearch: Bool = false @@ -201,11 +212,11 @@ public final class ChatListNavigationBar: Component { self.scrollTheme = component.theme self.scrollStrings = component.strings - let searchOffsetDistance: CGFloat = navigationBarSearchContentHeight - let defaultStoriesOffsetDistance: CGFloat = 79.0 + let searchOffsetDistance: CGFloat = ChatListNavigationBar.searchScrollHeight + let defaultStoriesOffsetDistance: CGFloat = ChatListNavigationBar.storiesScrollHeight let effectiveStoriesOffsetDistance: CGFloat - var minContentOffset: CGFloat = navigationBarSearchContentHeight + var minContentOffset: CGFloat = ChatListNavigationBar.searchScrollHeight if !component.isSearchActive, let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty, component.storiesUnlocked { effectiveStoriesOffsetDistance = defaultStoriesOffsetDistance * (1.0 - component.secondaryTransition) minContentOffset += effectiveStoriesOffsetDistance @@ -269,22 +280,32 @@ public final class ChatListNavigationBar: Component { self.addSubview(searchContentNode.view) } + let clippedStoriesOverscrollOffset = -min(0.0, clippedScrollOffset) let clippedStoriesOffset = max(0.0, min(clippedScrollOffset, defaultStoriesOffsetDistance)) var storiesOffsetFraction: CGFloat - if !component.isSearchActive, component.secondaryTransition == 0.0, let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty, component.storiesUnlocked { - storiesOffsetFraction = clippedStoriesOffset / defaultStoriesOffsetDistance + var storiesUnlockedOffsetFraction: CGFloat + if !component.isSearchActive, component.secondaryTransition == 0.0, let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty { + if component.storiesUnlocked { + storiesOffsetFraction = clippedStoriesOffset / defaultStoriesOffsetDistance + storiesUnlockedOffsetFraction = 1.0 + } else { + storiesOffsetFraction = 1.0 - (clippedStoriesOverscrollOffset / defaultStoriesOffsetDistance) + storiesUnlockedOffsetFraction = 0.0 + } } else { storiesOffsetFraction = 1.0 + storiesUnlockedOffsetFraction = 1.0 } if self.applyScrollFractionAnimator != nil { storiesOffsetFraction = self.applyScrollFraction * storiesOffsetFraction + (1.0 - self.applyScrollFraction) * 1.0 + storiesUnlockedOffsetFraction = self.applyScrollUnlockedFraction * storiesUnlockedOffsetFraction + (1.0 - self.applyScrollUnlockedFraction) * 1.0 } let searchSize = CGSize(width: currentLayout.size.width, height: navigationBarSearchContentHeight) var searchFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height - searchSize.height), size: searchSize) if component.tabsNode != nil { - searchFrame.origin.y -= 46.0 + searchFrame.origin.y -= 40.0 } let clippedSearchOffset = max(0.0, min(clippedScrollOffset - effectiveStoriesOffsetDistance, searchOffsetDistance)) @@ -300,6 +321,7 @@ public final class ChatListNavigationBar: Component { headerTransition = .immediate } + self.storiesOffsetFraction = storiesOffsetFraction let headerContentSize = self.headerContent.update( transition: headerTransition, component: AnyComponent(ChatListHeaderComponent( @@ -309,7 +331,9 @@ public final class ChatListNavigationBar: Component { secondaryTransition: component.secondaryTransition, networkStatus: nil, storySubscriptions: component.storySubscriptions, + storiesIncludeHidden: component.storiesIncludeHidden, storiesFraction: 1.0 - storiesOffsetFraction, + storiesUnlockedFraction: 1.0 - storiesUnlockedOffsetFraction, uploadProgress: component.uploadProgress, context: component.context, theme: component.theme, @@ -375,7 +399,7 @@ public final class ChatListNavigationBar: Component { if let disappearingTabsView = self.disappearingTabsView { disappearingTabsView.layer.anchorPoint = CGPoint() - transition.setFrameWithAdditivePosition(view: disappearingTabsView, frame: tabsFrame.offsetBy(dx: 0.0, dy: self.disappearingTabsViewSearch ? -56.0 : 0.0)) + transition.setFrameWithAdditivePosition(view: disappearingTabsView, frame: tabsFrame.offsetBy(dx: 0.0, dy: self.disappearingTabsViewSearch ? (-currentLayout.size.height + 2.0) : 0.0)) } if let tabsNode = component.tabsNode { @@ -401,7 +425,7 @@ public final class ChatListNavigationBar: Component { transition.setAlpha(view: tabsNode.view, alpha: 1.0) } - tabsNodeTransition.setFrameWithAdditivePosition(view: tabsNode.view, frame: tabsFrame) + tabsNodeTransition.setFrameWithAdditivePosition(view: tabsNode.view, frame: tabsFrame.offsetBy(dx: 0.0, dy: component.tabsNodeIsSearch ? (-currentLayout.size.height + 2.0) : 0.0)) } } @@ -441,7 +465,7 @@ public final class ChatListNavigationBar: Component { self.effectiveStoriesInsetHeight = 0.0 } else { if let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty, component.storiesUnlocked { - let storiesHeight: CGFloat = 79.0 * (1.0 - component.secondaryTransition) + let storiesHeight: CGFloat = ChatListNavigationBar.storiesScrollHeight * (1.0 - component.secondaryTransition) contentHeight += storiesHeight self.effectiveStoriesInsetHeight = storiesHeight } else { @@ -452,7 +476,7 @@ public final class ChatListNavigationBar: Component { } if component.tabsNode != nil { - contentHeight += 46.0 + contentHeight += 40.0 } let size = CGSize(width: availableSize.width, height: contentHeight) @@ -467,13 +491,18 @@ public final class ChatListNavigationBar: Component { } if storiesUnlockedUpdated && component.storiesUnlocked { - self.applyScrollFraction = 0.0 self.applyScrollStartFraction = 0.0 + let startFraction = self.storiesOffsetFraction + self.applyScrollFraction = (1.0 - 0.0) * startFraction + 0.0 * 1.0 + self.applyScrollUnlockedFraction = 0.0 self.applyScrollFractionAnimator = DisplayLinkAnimator(duration: 0.3, from: 0.0, to: 1.0, update: { [weak self] value in guard let self else { return } - self.applyScrollFraction = listViewAnimationCurveSystem(value) + + let t = listViewAnimationCurveSystem(value) + self.applyScrollFraction = (1.0 - t) * startFraction + t * 1.0 + self.applyScrollUnlockedFraction = t if let rawScrollOffset = self.rawScrollOffset { self.hasDeferredScrollOffset = true diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index 3d3cb07b71..9eeb44fddc 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -372,9 +372,10 @@ private final class StoryContainerScreenComponent: Component { func animateOut(completion: @escaping () -> Void) { self.isAnimatingOut = true - self.state?.updated(transition: .immediate) if 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.id) { + self.state?.updated(transition: .immediate) + let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut)) transition.setAlpha(layer: self.backgroundLayer, alpha: 0.0) transition.setAlpha(view: self.backgroundEffectView, alpha: 0.0) @@ -385,10 +386,14 @@ private final class StoryContainerScreenComponent: Component { transitionOutCompleted() }) } else { - self.layer.allowsGroupOpacity = true - self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + self.dismissPanState = ItemSetPanState(fraction: 1.0, didBegin: true) + self.state?.updated(transition: Transition(animation: .curve(duration: 0.2, curve: .easeInOut))) + + let transition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut)) + transition.setAlpha(layer: self.backgroundLayer, alpha: 0.0, completion: { _ in completion() }) + transition.setAlpha(view: self.backgroundEffectView, alpha: 0.0) } self.didAnimateOut = true diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 53b2046a22..b88a6377f7 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1061,8 +1061,31 @@ public final class StoryItemSetContainerComponent: Component { if component.slice.item.storyItem.isPublic { items.append(.action(ContextMenuActionItem(text: "Copy link", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) - }, action: { _, a in + }, action: { [weak self] _, a in a(.default) + + guard let self, let component = self.component else { + return + } + + let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id) + |> deliverOnMainQueue).start(next: { [weak self] link in + guard let self, let component = self.component else { + return + } + if let link { + UIPasteboard.general.string = link + + let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) + component.presentController(UndoOverlayController( + presentationData: presentationData, + content: .linkCopied(text: "Link copied."), + elevatedLayout: false, + animateInAsReplacement: false, + action: { _ in return false } + )) + } + }) }))) items.append(.action(ContextMenuActionItem(text: "Share", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) @@ -1652,9 +1675,9 @@ public final class StoryItemSetContainerComponent: Component { ) if let inlineActionsView = self.inlineActions.view { if inlineActionsView.superview == nil { - //self.contentContainerView.addSubview(inlineActionsView) + self.contentContainerView.addSubview(inlineActionsView) } - transition.setFrame(view: inlineActionsView, frame: CGRect(origin: CGPoint(x: contentFrame.width - 10.0 - inlineActionsSize.width, y: contentFrame.height - 20.0 - inlineActionsSize.height), size: inlineActionsSize)) + transition.setFrame(view: inlineActionsView, frame: CGRect(origin: CGPoint(x: contentFrame.width - 10.0 - inlineActionsSize.width, y: contentFrame.height - 10.0 - inlineActionsSize.height), size: inlineActionsSize)) var inlineActionsAlpha: CGFloat = inputPanelIsOverlay ? 0.0 : 1.0 if self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil { @@ -1666,6 +1689,9 @@ public final class StoryItemSetContainerComponent: Component { if component.hideUI || self.displayViewList { inlineActionsAlpha = 0.0 } + if !component.slice.item.storyItem.isPublic { + inlineActionsAlpha = 0.0 + } transition.setAlpha(view: inlineActionsView, alpha: inlineActionsAlpha) } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index ca68e59709..edbf991622 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -273,40 +273,26 @@ final class StoryItemSetContainerSendMessage { } func performInlineAction(view: StoryItemSetContainerComponent.View, item: StoryActionsComponent.Item) { - /*guard let component = view.component else { + guard let component = view.component else { return } - guard let focusedItemId = view.focusedItemId, let focusedItem = view.currentSlice?.items.first(where: { $0.id == focusedItemId }) else { + let focusedItem = component.slice.item + guard let peerId = focusedItem.peerId else { return } switch item.kind { case .like: - if item.isActivated { - component.context.engine.messages.setMessageReactions( - id: targetMessageId, - reactions: [ - ] - ) - } else { - component.context.engine.messages.setMessageReactions( - id: targetMessageId, - reactions: [ - .builtin("❤") - ] - ) - } + return case .share: - let _ = (component.context.engine.data.get( - TelegramEngine.EngineData.Item.Messages.Message(id: targetMessageId) - ) - |> deliverOnMainQueue).start(next: { [weak view] message in - guard let view, let message, let component = view.component, let controller = component.controller() else { + let _ = (component.context.engine.messages.exportStoryLink(peerId: peerId, id: focusedItem.storyItem.id) + |> deliverOnMainQueue).start(next: { [weak view] link in + guard let view, let link, let component = view.component, let controller = component.controller() else { return } let shareController = ShareController( context: component.context, - subject: .messages([message._asMessage()]), + subject: .url(link), externalShare: false, immediateExternalShare: false, updatedPresentationData: (component.context.sharedContext.currentPresentationData.with({ $0 }), @@ -314,7 +300,7 @@ final class StoryItemSetContainerSendMessage { ) controller.present(shareController, in: .window(.root)) }) - }*/ + } } private func clearInputText(view: StoryItemSetContainerComponent.View) { diff --git a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift index 6b52b9fed2..4dc014564d 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift @@ -310,6 +310,7 @@ public final class StoryContentContextImpl: StoryContentContext { } private let context: AccountContext + private let includeHidden: Bool public private(set) var stateValue: StoryContentContextState? public var state: Signal { @@ -342,14 +343,16 @@ public final class StoryContentContextImpl: StoryContentContext { public init( context: AccountContext, + includeHidden: Bool, focusedPeerId: EnginePeer.Id? ) { self.context = context + self.includeHidden = includeHidden if let focusedPeerId { self.focusedItem = (focusedPeerId, nil) } - self.storySubscriptionsDisposable = (context.engine.messages.storySubscriptions() + self.storySubscriptionsDisposable = (context.engine.messages.storySubscriptions(includeHidden: includeHidden) |> deliverOnMainQueue).start(next: { [weak self] storySubscriptions in guard let self else { return @@ -741,6 +744,16 @@ public final class SingleStoryContentContextImpl: StoryContentContext { guard let self else { return } + + if item == nil { + let storyKey = StoryKey(peerId: storyId.peerId, id: storyId.id) + if !self.requestedStoryKeys.contains(storyKey) { + self.requestedStoryKeys.insert(storyKey) + + self.requestStoryDisposables.add(self.context.engine.messages.refreshStories(peerId: storyId.peerId, ids: [storyId.id]).start()) + } + } + if let item, case let .item(itemValue) = item, let media = itemValue.media, let peer { let mappedItem = EngineStoryItem( id: itemValue.id, diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift index 799fde5cae..a85c31bead 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift @@ -21,8 +21,10 @@ public final class StoryPeerListComponent: Component { public let context: AccountContext public let theme: PresentationTheme public let strings: PresentationStrings + public let includesHidden: Bool public let storySubscriptions: EngineStorySubscriptions? public let collapseFraction: CGFloat + public let unlockedFraction: CGFloat public let uploadProgress: Float? public let peerAction: (EnginePeer?) -> Void public let contextPeerAction: (ContextExtractedContentContainingNode, ContextGesture, EnginePeer) -> Void @@ -32,8 +34,10 @@ public final class StoryPeerListComponent: Component { context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, + includesHidden: Bool, storySubscriptions: EngineStorySubscriptions?, collapseFraction: CGFloat, + unlockedFraction: CGFloat, uploadProgress: Float?, peerAction: @escaping (EnginePeer?) -> Void, contextPeerAction: @escaping (ContextExtractedContentContainingNode, ContextGesture, EnginePeer) -> Void @@ -42,8 +46,10 @@ public final class StoryPeerListComponent: Component { self.context = context self.theme = theme self.strings = strings + self.includesHidden = includesHidden self.storySubscriptions = storySubscriptions self.collapseFraction = collapseFraction + self.unlockedFraction = unlockedFraction self.uploadProgress = uploadProgress self.peerAction = peerAction self.contextPeerAction = contextPeerAction @@ -59,12 +65,18 @@ public final class StoryPeerListComponent: Component { if lhs.strings !== rhs.strings { return false } + if lhs.includesHidden != rhs.includesHidden { + return false + } if lhs.storySubscriptions != rhs.storySubscriptions { return false } if lhs.collapseFraction != rhs.collapseFraction { return false } + if lhs.unlockedFraction != rhs.unlockedFraction { + return false + } if lhs.uploadProgress != rhs.uploadProgress { return false } @@ -211,7 +223,7 @@ public final class StoryPeerListComponent: Component { } let _ = hasStories - let collapseStartIndex = 1 + let collapseStartIndex = component.includesHidden ? 0 : 1 let collapsedItemWidth: CGFloat = 24.0 let collapsedItemDistance: CGFloat = 14.0 @@ -290,7 +302,14 @@ public final class StoryPeerListComponent: Component { let itemFrame: CGRect if isReallyVisible { - itemFrame = regularItemFrame.interpolate(to: collapsedItemFrame, amount: component.collapseFraction) + var adjustedRegularFrame = regularItemFrame + if i < collapseStartIndex { + adjustedRegularFrame = adjustedRegularFrame.interpolate(to: itemLayout.frame(at: collapseStartIndex), amount: 1.0 - component.unlockedFraction) + } else if i > collapseEndIndex { + adjustedRegularFrame = adjustedRegularFrame.interpolate(to: itemLayout.frame(at: collapseEndIndex), amount: 1.0 - component.unlockedFraction) + } + + itemFrame = adjustedRegularFrame.interpolate(to: collapsedItemFrame, amount: component.collapseFraction) } else { itemFrame = regularItemFrame } @@ -315,7 +334,7 @@ public final class StoryPeerListComponent: Component { rightItemFrame = regularRightItemFrame.interpolate(to: collapsedRightItemFrame, amount: component.collapseFraction) } } else { - if component.collapseFraction == 1.0 { + if component.collapseFraction == 1.0 || component.unlockedFraction == 0.0 { itemAlpha = 0.0 } else { itemAlpha = 1.0 @@ -440,8 +459,15 @@ public final class StoryPeerListComponent: Component { if let storySubscriptions = component.storySubscriptions, let hasMoreToken = storySubscriptions.hasMoreToken { if self.requestedLoadMoreToken != hasMoreToken { self.requestedLoadMoreToken = hasMoreToken - if let storySubscriptionsContext = component.context.account.storySubscriptionsContext { - storySubscriptionsContext.loadMore() + + if component.includesHidden { + if let storySubscriptionsContext = component.context.account.filteredStorySubscriptionsContext { + storySubscriptionsContext.loadMore() + } + } else { + if let storySubscriptionsContext = component.context.account.allStorySubscriptionsContext { + storySubscriptionsContext.loadMore() + } } } } @@ -450,7 +476,7 @@ public final class StoryPeerListComponent: Component { self.sortedItems.removeAll(keepingCapacity: true) if let storySubscriptions = component.storySubscriptions { - if let accountItem = storySubscriptions.accountItem { + if !component.includesHidden, let accountItem = storySubscriptions.accountItem { self.sortedItems.append(accountItem) } diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift index 7e821f79f1..fb6eba007e 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift @@ -388,7 +388,7 @@ public final class StoryPeerListItemComponent: Component { let effectiveWidth: CGFloat = (1.0 - component.collapseFraction) * availableSize.width + component.collapseFraction * component.collapsedWidth - let effectiveScale: CGFloat = 1.0 * (1.0 - component.collapseFraction) + (25.0 / 52.0) * component.collapsedScaleFactor * component.collapseFraction + let effectiveScale: CGFloat = 1.0 * (1.0 - component.collapseFraction) + (24.0 / 52.0) * component.collapsedScaleFactor * component.collapseFraction let avatarNode: AvatarNode if let current = self.avatarNode { @@ -577,7 +577,7 @@ public final class StoryPeerListItemComponent: Component { environment: {}, containerSize: CGSize(width: availableSize.width + 4.0, height: 100.0) ) - let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5) + (effectiveWidth - availableSize.width) * 0.5, y: indicatorFrame.midY + (indicatorFrame.height * 0.5 + 3.0) * effectiveScale), size: titleSize) + let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5) + (effectiveWidth - availableSize.width) * 0.5, y: indicatorFrame.midY + (indicatorFrame.height * 0.5 + 2.0) * effectiveScale), size: titleSize) if let titleView = self.title.view { if titleView.superview == nil { titleView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5) diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 9f7905a351..d4c8c17311 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -1183,7 +1183,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode ignoreForward = true effectiveAuthor = forwardInfo.author if effectiveAuthor == nil, let authorSignature = forwardInfo.authorSignature { - effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) } } displayAuthorInfo = !mergedTop.merged && incoming && effectiveAuthor != nil @@ -1199,7 +1199,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode displayAuthorInfo = !mergedTop.merged && incoming } else if let forwardInfo = item.content.firstMessage.forwardInfo, forwardInfo.flags.contains(.isImported), let authorSignature = forwardInfo.authorSignature { ignoreForward = true - effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) displayAuthorInfo = !mergedTop.merged && incoming } else if let adAttribute = item.content.firstMessage.adAttribute, let author = item.content.firstMessage.author { ignoreForward = true diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift index 269f23b620..dace06728c 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift @@ -338,7 +338,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { if let forwardInfo = content.firstMessage.forwardInfo { effectiveAuthor = forwardInfo.author if effectiveAuthor == nil, let authorSignature = forwardInfo.authorSignature { - effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) } } displayAuthorInfo = incoming && effectiveAuthor != nil diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index da7ab00b4e..ddfa0fa079 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -978,6 +978,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { break case .gameStart: break + case .story: + break case let .channelMessage(peer, messageId, timecode): if let navigationController = strongSelf.getNavigationController() { strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(EnginePeer(peer)), subject: .message(id: .id(messageId), highlight: true, timecode: timecode))) diff --git a/submodules/TelegramUI/Sources/ComposeControllerNode.swift b/submodules/TelegramUI/Sources/ComposeControllerNode.swift index 00e413ddf1..270dec9376 100644 --- a/submodules/TelegramUI/Sources/ComposeControllerNode.swift +++ b/submodules/TelegramUI/Sources/ComposeControllerNode.swift @@ -109,7 +109,7 @@ final class ComposeControllerNode: ASDisplayNode { searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) } - self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition) + self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, storiesInset: 0.0, transition: transition) self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size) } diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift index abb213f519..bdb67b2561 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift @@ -231,7 +231,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { var headerInsets = layout.insets(options: [.input]) headerInsets.top += actualNavigationBarHeight headerInsets.top += strongSelf.tokenListNode.bounds.size.height - searchResultsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: .immediate) + searchResultsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, storiesInset: 0.0, transition: .immediate) searchResultsNode.frame = CGRect(origin: CGPoint(), size: layout.size) } @@ -285,7 +285,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { switch self.contentNode { case let .contacts(contactsNode): - contactsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition) + contactsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, storiesInset: 0.0, transition: transition) case let .chats(chatsNode): var combinedInsets = insets combinedInsets.left += layout.safeInsets.left @@ -297,7 +297,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { self.contentNode.node.frame = CGRect(origin: CGPoint(), size: layout.size) if let searchResultsNode = self.searchResultsNode { - searchResultsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition) + searchResultsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, storiesInset: 0.0, transition: transition) searchResultsNode.frame = CGRect(origin: CGPoint(), size: layout.size) } diff --git a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift index b0c1c61aa2..8c4364964e 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift @@ -144,7 +144,7 @@ final class ContactSelectionControllerNode: ASDisplayNode { searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) } - self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition) + self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, storiesInset: 0.0, transition: transition) self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size) diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 52b5f91c33..a97f9007b9 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -30,6 +30,8 @@ import BotPaymentsUI import PremiumUI import AuthorizationUI import ChatFolderLinkPreviewScreen +import StoryContainerScreen +import StoryContentComponent private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer { if case .default = navigation { @@ -803,5 +805,24 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur })) dismissInput() } + case let .story(peerId, id): + let storyContent = SingleStoryContentContextImpl(context: context, storyId: StoryId(peerId: peerId, id: id)) + let _ = (storyContent.state + |> take(1) + |> deliverOnMainQueue).start(next: { [weak navigationController] _ in + let transitionIn: StoryContainerScreen.TransitionIn? = nil + + let storyContainerScreen = StoryContainerScreen( + context: context, + content: storyContent, + transitionIn: transitionIn, + transitionOut: { _, _ in + let transitionOut: StoryContainerScreen.TransitionOut? = nil + + return transitionOut + } + ) + navigationController?.pushViewController(storyContainerScreen) + }) } } diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index be89561c8c..9ec9474264 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -1069,7 +1069,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { contactListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) contactListNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0) - contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition) + contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, storiesInset: 0.0, transition: transition) } if let searchDisplayController = self.searchDisplayController { diff --git a/submodules/TelegramUI/Sources/PollResultsController.swift b/submodules/TelegramUI/Sources/PollResultsController.swift index 7642dab1cc..131364aa82 100644 --- a/submodules/TelegramUI/Sources/PollResultsController.swift +++ b/submodules/TelegramUI/Sources/PollResultsController.swift @@ -250,7 +250,7 @@ private func pollResultsControllerEntries(presentationData: PresentationData, po displayCount = Int(voterCount) } for peerIndex in 0 ..< displayCount { - let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []) + let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil) let peer = EngineRenderedPeer(peer: EnginePeer(fakeUser)) entries.append(.optionPeer(optionId: i, index: peerIndex, peer: peer, optionText: optionTextHeader, optionAdditionalText: optionAdditionalTextHeader, optionCount: voterCount, optionExpanded: false, opaqueIdentifier: option.opaqueIdentifier, shimmeringAlternation: peerIndex % 2, isFirstInOption: peerIndex == 0)) } diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index ea4eccf27c..8045be8cbd 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -72,6 +72,7 @@ public enum ParsedInternalPeerUrlParameter { case replyThread(Int32, Int32) case voiceChat(String?) case appStart(String, String?) + case story(Int32) } public enum ParsedInternalUrl { @@ -246,6 +247,10 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { } } return .startAttach(peerName, value, choose) + } else if queryItem.name == "story" { + if let id = Int32(value) { + return .peer(.name(peerName), .story(id)) + } } } else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) { return .peer(.name(peerName), .voiceChat(nil)) @@ -697,6 +702,8 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) } case let .voiceChat(invite): return .single(.joinVoiceChat(peer.id, invite)) + case let .story(id): + return .single(.story(peerId: peer.id, id: id)) } } else { return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))