mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Stories
This commit is contained in:
parent
7dc3dc078e
commit
af19d3f4b5
@ -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 {
|
||||
|
@ -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)
|
||||
})))
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<ChatListNodeState.ItemId?>(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
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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<Bool>()
|
||||
public var ready: Signal<Bool, NoError> {
|
||||
@ -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])
|
||||
|
@ -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<ContactsSortOrder>()
|
||||
private let isInVoiceOver = ValuePromise<Bool>(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 {
|
||||
if let searchContentNode = self.searchContentNode() {
|
||||
self.contactsNode.activateSearch(placeholderNode: searchContentNode.placeholderNode)
|
||||
}
|
||||
self.setDisplayNavigationBar(false, transition: .animated(duration: 0.5, curve: .spring))
|
||||
}
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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<Empty>()
|
||||
|
||||
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<Bool>()
|
||||
|
||||
init(context: AccountContext, sortOrder: Signal<ContactsSortOrder, NoError>, 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<EnginePeer.Id>()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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<PeerId, Peer>()
|
||||
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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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?) {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
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() {
|
||||
|
@ -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,8 +19,9 @@ final class MutableStorySubscriptionsView: MutablePostboxView {
|
||||
if !transaction.storySubscriptionsEvents.isEmpty {
|
||||
loop: for event in transaction.storySubscriptionsEvents {
|
||||
switch event {
|
||||
case .replaceAll:
|
||||
let peerIds = postbox.storySubscriptionsTable.getAll()
|
||||
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
|
||||
@ -23,11 +31,12 @@ final class MutableStorySubscriptionsView: MutablePostboxView {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -163,8 +163,8 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
||||
let otherPeerId = self.context.account.peerId
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
var messages = SimpleDictionary<MessageId, Message>()
|
||||
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: [:])
|
||||
|
@ -145,7 +145,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
let messages = SimpleDictionary<MessageId, Message>()
|
||||
|
||||
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: [])
|
||||
|
||||
|
@ -274,7 +274,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
let messages = SimpleDictionary<MessageId, Message>()
|
||||
|
||||
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
|
||||
|
||||
|
@ -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<PeerId, Peer>()
|
||||
var messages = SimpleDictionary<MessageId, Message>()
|
||||
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: [:])
|
||||
|
@ -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<PeerId, Peer>()
|
||||
var messages = SimpleDictionary<MessageId, Message>()
|
||||
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] = []
|
||||
|
||||
|
@ -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<PeerId, Peer>()
|
||||
var messages = SimpleDictionary<MessageId, Message>()
|
||||
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] = []
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -1485,8 +1485,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
let otherPeerId = self.context.account.peerId
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
let messages = SimpleDictionary<MessageId, Message>()
|
||||
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 = ""
|
||||
|
@ -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<TelegramAccountManagerTypes>, 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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)))
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -1290,3 +1290,53 @@ public final class EngineStoryViewListContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_updatePeerStoriesHidden(account: Account, id: PeerId, isHidden: Bool) -> Signal<Never, NoError> {
|
||||
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<Never, NoError> in
|
||||
guard let inputUser = inputUser else {
|
||||
return .complete()
|
||||
}
|
||||
return account.network.request(Api.functions.contacts.toggleStoriesHidden(id: inputUser, hidden: isHidden ? .boolTrue : .boolFalse))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_exportStoryLink(account: Account, peerId: EnginePeer.Id, id: Int32) -> Signal<String?, NoError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputUser? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputUser)
|
||||
}
|
||||
|> mapToSignal { inputUser -> Signal<String?, NoError> 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<Api.ExportedStoryLink?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result -> String? in
|
||||
guard let result = result else {
|
||||
return nil
|
||||
}
|
||||
switch result {
|
||||
case let .exportedStoryLink(link):
|
||||
return link
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Impl>
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -592,7 +592,7 @@ public extension TelegramEngine {
|
||||
}).start()
|
||||
}
|
||||
|
||||
public func storySubscriptions() -> Signal<EngineStorySubscriptions, NoError> {
|
||||
public func storySubscriptions(includeHidden: Bool) -> Signal<EngineStorySubscriptions, NoError> {
|
||||
let debugTimerSignal: Signal<Bool, NoError>
|
||||
#if DEBUG && false
|
||||
debugTimerSignal = Signal<Bool, NoError>.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<EngineStorySubscriptions, NoError> 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<String?, NoError> {
|
||||
return _internal_exportStoryLink(account: self.account, peerId: peerId, id: id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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<Empty>
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
@ -117,6 +123,9 @@ public final class ChatListNavigationBar: Component {
|
||||
}
|
||||
}
|
||||
|
||||
public static let searchScrollHeight: CGFloat = 52.0
|
||||
public static let storiesScrollHeight: CGFloat = 94.0
|
||||
|
||||
public final class View: UIView {
|
||||
private let backgroundView: BlurredBackgroundView
|
||||
private let separatorLayer: SimpleLayer
|
||||
@ -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 {
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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<StoryContentContextState, NoError> {
|
||||
@ -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,
|
||||
|
@ -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,9 +459,16 @@ 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 {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)))
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)))
|
||||
|
Loading…
x
Reference in New Issue
Block a user