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 invoice(slug: String, invoice: TelegramMediaInvoice?)
|
||||||
case premiumOffer(reference: String?)
|
case premiumOffer(reference: String?)
|
||||||
case chatFolder(slug: String)
|
case chatFolder(slug: String)
|
||||||
|
case story(peerId: PeerId, id: Int32)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum NavigateToChatKeepStack {
|
public enum NavigateToChatKeepStack {
|
||||||
|
@ -326,9 +326,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
case let .known(offset):
|
case let .known(offset):
|
||||||
let isFirstFilter = strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.chatListFilter == strongSelf.chatListDisplayNode.mainContainerNode.availableFilters.first?.filter
|
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)
|
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 firstFilter = strongSelf.chatListDisplayNode.effectiveContainerNode.availableFilters.first ?? .all
|
||||||
let targetTab: ChatListFilterTabEntryId
|
let targetTab: ChatListFilterTabEntryId
|
||||||
switch firstFilter {
|
switch firstFilter {
|
||||||
@ -1244,7 +1244,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let storyContent = StoryContentContextImpl(context: self.context, focusedPeerId: peerId)
|
let storyContent = StoryContentContextImpl(context: self.context, includeHidden: false, focusedPeerId: peerId)
|
||||||
let _ = (storyContent.state
|
let _ = (storyContent.state
|
||||||
|> filter { $0.slice != nil }
|
|> filter { $0.slice != nil }
|
||||||
|> take(1)
|
|> take(1)
|
||||||
@ -1696,7 +1696,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
|
|
||||||
if case .chatList(.root) = self.location {
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] resources in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -1729,7 +1729,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
self.preloadStoryResourceDisposables.removeValue(forKey: id)
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] storySubscriptions in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -2313,10 +2313,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
|
|
||||||
self.validLayout = layout
|
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)
|
self.updateLayout(layout: layout, transition: transition)
|
||||||
|
|
||||||
if layout.inVoiceOver != wasInVoiceOver {
|
if layout.inVoiceOver != wasInVoiceOver {
|
||||||
@ -2329,7 +2325,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let storyContent = StoryContentContextImpl(context: self.context, focusedPeerId: peer?.id)
|
let storyContent = StoryContentContextImpl(context: self.context, includeHidden: false, focusedPeerId: peer?.id)
|
||||||
let _ = (storyContent.state
|
let _ = (storyContent.state
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] storyContentState in
|
|> 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)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unmute"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
})))
|
})))*/
|
||||||
items.append(.action(ContextMenuActionItem(text: "Archive", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: "Archive", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { _, f in
|
}, action: { [weak self] _, f in
|
||||||
f(.default)
|
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 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 timestamp1: Int32 = 100000
|
||||||
let peers: [EnginePeer.Id: EnginePeer] = [:]
|
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
|
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)
|
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 {
|
if let controller, case .chatList(groupId: .root) = controller.location {
|
||||||
self.listNode.scrollHeightTopInset = navigationBarSearchContentHeight + 79.0
|
self.listNode.scrollHeightTopInset = ChatListNavigationBar.searchScrollHeight + ChatListNavigationBar.storiesScrollHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
@ -838,6 +838,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
previousItemNode.listNode.openStories = nil
|
previousItemNode.listNode.openStories = nil
|
||||||
previousItemNode.listNode.addedVisibleChatsWithPeerIds = nil
|
previousItemNode.listNode.addedVisibleChatsWithPeerIds = nil
|
||||||
previousItemNode.listNode.didBeginSelectingChats = nil
|
previousItemNode.listNode.didBeginSelectingChats = nil
|
||||||
|
previousItemNode.listNode.canExpandHiddenItems = nil
|
||||||
|
|
||||||
previousItemNode.accessibilityElementsHidden = true
|
previousItemNode.accessibilityElementsHidden = true
|
||||||
}
|
}
|
||||||
@ -914,7 +915,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
if itemNode.listNode.isTracking {
|
if itemNode.listNode.isTracking {
|
||||||
if case let .known(value) = offset {
|
if case let .known(value) = offset {
|
||||||
if !self.storiesUnlocked {
|
if !self.storiesUnlocked {
|
||||||
if value < -50.0 {
|
if value < -40.0 {
|
||||||
self.storiesUnlocked = true
|
self.storiesUnlocked = true
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -935,7 +936,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
} else if self.storiesUnlocked {
|
} else if self.storiesUnlocked {
|
||||||
switch offset {
|
switch offset {
|
||||||
case let .known(value):
|
case let .known(value):
|
||||||
if value >= 79.0 {
|
if value >= ChatListNavigationBar.storiesScrollHeight {
|
||||||
self.storiesUnlocked = false
|
self.storiesUnlocked = false
|
||||||
self.onStoriesLockedUpdated?(false)
|
self.onStoriesLockedUpdated?(false)
|
||||||
}
|
}
|
||||||
@ -950,7 +951,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
}
|
}
|
||||||
switch self.currentItemNode.visibleContentOffset() {
|
switch self.currentItemNode.visibleContentOffset() {
|
||||||
case let .known(value):
|
case let .known(value):
|
||||||
if value > 79.0 {
|
if value > ChatListNavigationBar.storiesScrollHeight {
|
||||||
if self.storiesUnlocked {
|
if self.storiesUnlocked {
|
||||||
self.storiesUnlocked = false
|
self.storiesUnlocked = false
|
||||||
|
|
||||||
@ -986,6 +987,12 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
itemNode.listNode.didBeginSelectingChats = { [weak self] in
|
itemNode.listNode.didBeginSelectingChats = { [weak self] in
|
||||||
self?.didBeginSelectingChats?()
|
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
|
self.currentItemStateValue.set(itemNode.listNode.state |> map { state in
|
||||||
let filterId: Int32?
|
let filterId: Int32?
|
||||||
@ -1047,6 +1054,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
var openStories: ((EnginePeer.Id) -> Void)?
|
var openStories: ((EnginePeer.Id) -> Void)?
|
||||||
var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
|
var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
|
||||||
var didBeginSelectingChats: (() -> Void)?
|
var didBeginSelectingChats: (() -> Void)?
|
||||||
|
var canExpandHiddenItems: (() -> Bool)?
|
||||||
public var displayFilterLimit: (() -> Void)?
|
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) {
|
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 {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//self.controller?.requestLayout(transition: .immediate)
|
if isLocked {
|
||||||
self.controller?.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
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
|
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,
|
secondaryContent: headerContent?.secondaryContent,
|
||||||
secondaryTransition: self.inlineStackContainerTransitionFraction,
|
secondaryTransition: self.inlineStackContainerTransitionFraction,
|
||||||
storySubscriptions: self.controller?.storySubscriptions,
|
storySubscriptions: self.controller?.storySubscriptions,
|
||||||
|
storiesIncludeHidden: false,
|
||||||
uploadProgress: self.controller?.storyUploadProgress,
|
uploadProgress: self.controller?.storyUploadProgress,
|
||||||
tabsNode: tabsNode,
|
tabsNode: tabsNode,
|
||||||
tabsNodeIsSearch: tabsNodeIsSearch,
|
tabsNodeIsSearch: tabsNodeIsSearch,
|
||||||
@ -1948,7 +1972,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
var storiesInset = storiesInset
|
var storiesInset = storiesInset
|
||||||
|
|
||||||
let navigationBarLayout = self.updateNavigationBar(layout: layout, transition: transition)
|
let navigationBarLayout = self.updateNavigationBar(layout: layout, transition: transition)
|
||||||
self.mainContainerNode.initialScrollingOffset = navigationBarSearchContentHeight + navigationBarLayout.storiesInset
|
self.mainContainerNode.initialScrollingOffset = ChatListNavigationBar.searchScrollHeight + navigationBarLayout.storiesInset
|
||||||
|
|
||||||
navigationBarHeight = navigationBarLayout.navigationHeight
|
navigationBarHeight = navigationBarLayout.navigationHeight
|
||||||
visualNavigationHeight = navigationBarLayout.navigationHeight
|
visualNavigationHeight = navigationBarLayout.navigationHeight
|
||||||
@ -2146,8 +2170,8 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
self.mainContainerNode.accessibilityElementsHidden = false
|
self.mainContainerNode.accessibilityElementsHidden = false
|
||||||
self.inlineStackContainerNode?.accessibilityElementsHidden = false
|
self.inlineStackContainerNode?.accessibilityElementsHidden = false
|
||||||
|
|
||||||
return { [weak self] in
|
return { [weak self, weak placeholderNode] in
|
||||||
if let strongSelf = self, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout {
|
if let strongSelf = self, let placeholderNode, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout {
|
||||||
searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated)
|
searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated)
|
||||||
|
|
||||||
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
|
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
|
||||||
@ -2242,21 +2266,21 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
let searchScrollOffset = clippedScrollOffset - navigationBarComponentView.effectiveStoriesInsetHeight
|
let searchScrollOffset = clippedScrollOffset - navigationBarComponentView.effectiveStoriesInsetHeight
|
||||||
if searchScrollOffset > 0.0 && searchScrollOffset < navigationBarSearchContentHeight {
|
if searchScrollOffset > 0.0 && searchScrollOffset < ChatListNavigationBar.searchScrollHeight {
|
||||||
if searchScrollOffset < navigationBarSearchContentHeight * 0.5 {
|
if searchScrollOffset < ChatListNavigationBar.searchScrollHeight * 0.5 {
|
||||||
let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight, animated: true)
|
let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight, animated: true)
|
||||||
} else {
|
} else {
|
||||||
let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight + navigationBarSearchContentHeight, animated: true)
|
let _ = listView.scrollToOffsetFromTop(navigationBarComponentView.effectiveStoriesInsetHeight + ChatListNavigationBar.searchScrollHeight, animated: true)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if clippedScrollOffset > 0.0 && clippedScrollOffset < navigationBarSearchContentHeight {
|
if clippedScrollOffset > 0.0 && clippedScrollOffset < ChatListNavigationBar.searchScrollHeight {
|
||||||
if clippedScrollOffset < navigationBarSearchContentHeight * 0.5 {
|
if clippedScrollOffset < ChatListNavigationBar.searchScrollHeight * 0.5 {
|
||||||
let _ = listView.scrollToOffsetFromTop(0.0, animated: true)
|
let _ = listView.scrollToOffsetFromTop(0.0, animated: true)
|
||||||
} else {
|
} else {
|
||||||
let _ = listView.scrollToOffsetFromTop(navigationBarSearchContentHeight, animated: true)
|
let _ = listView.scrollToOffsetFromTop(ChatListNavigationBar.searchScrollHeight, animated: true)
|
||||||
}
|
}
|
||||||
return 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 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 timestamp1: Int32 = 100000
|
||||||
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||||
peers[peer1.id] = peer1
|
peers[peer1.id] = peer1
|
||||||
|
@ -19,6 +19,7 @@ import MultiAnimationRenderer
|
|||||||
import Postbox
|
import Postbox
|
||||||
import ChatFolderLinkPreviewScreen
|
import ChatFolderLinkPreviewScreen
|
||||||
import StoryContainerScreen
|
import StoryContainerScreen
|
||||||
|
import ChatListHeaderComponent
|
||||||
|
|
||||||
public enum ChatListNodeMode {
|
public enum ChatListNodeMode {
|
||||||
case chatList(appendContacts: Bool)
|
case chatList(appendContacts: Bool)
|
||||||
@ -1154,6 +1155,8 @@ public final class ChatListNode: ListView {
|
|||||||
public var isEmptyUpdated: ((ChatListNodeEmptyState, Bool, ContainedViewLayoutTransition) -> Void)?
|
public var isEmptyUpdated: ((ChatListNodeEmptyState, Bool, ContainedViewLayoutTransition) -> Void)?
|
||||||
private var currentIsEmptyState: ChatListNodeEmptyState?
|
private var currentIsEmptyState: ChatListNodeEmptyState?
|
||||||
|
|
||||||
|
public var canExpandHiddenItems: (() -> Bool)?
|
||||||
|
|
||||||
public var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
|
public var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
|
||||||
|
|
||||||
private let currentRemovingItemId = Atomic<ChatListNodeState.ItemId?>(value: nil)
|
private let currentRemovingItemId = Atomic<ChatListNodeState.ItemId?>(value: nil)
|
||||||
@ -1210,7 +1213,7 @@ public final class ChatListNode: ListView {
|
|||||||
|
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
|
||||||
self.scrollHeightTopInset = navigationBarSearchContentHeight
|
self.scrollHeightTopInset = ChatListNavigationBar.searchScrollHeight
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -2379,11 +2382,11 @@ public final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isHiddenItemVisible && strongSelf.currentState.hiddenItemShouldBeTemporaryRevealed {
|
if !isHiddenItemVisible && strongSelf.currentState.hiddenItemShouldBeTemporaryRevealed {
|
||||||
strongSelf.updateState { state in
|
/*strongSelf.updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
state.hiddenItemShouldBeTemporaryRevealed = false
|
state.hiddenItemShouldBeTemporaryRevealed = false
|
||||||
return state
|
return state
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2717,6 +2720,7 @@ public final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var startedScrollingAtUpperBound = false
|
var startedScrollingAtUpperBound = false
|
||||||
|
var startedScrollingWithCanExpandHiddenItems = false
|
||||||
|
|
||||||
self.beganInteractiveDragging = { [weak self] _ in
|
self.beganInteractiveDragging = { [weak self] _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -2728,6 +2732,13 @@ public final class ChatListNode: ListView {
|
|||||||
case let .known(value):
|
case let .known(value):
|
||||||
startedScrollingAtUpperBound = value <= 0.0
|
startedScrollingAtUpperBound = value <= 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let canExpandHiddenItems = strongSelf.canExpandHiddenItems {
|
||||||
|
startedScrollingWithCanExpandHiddenItems = canExpandHiddenItems()
|
||||||
|
} else {
|
||||||
|
startedScrollingWithCanExpandHiddenItems = true
|
||||||
|
}
|
||||||
|
|
||||||
if strongSelf.currentState.peerIdWithRevealedOptions != nil {
|
if strongSelf.currentState.peerIdWithRevealedOptions != nil {
|
||||||
strongSelf.updateState { state in
|
strongSelf.updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
@ -2784,7 +2795,7 @@ public final class ChatListNode: ListView {
|
|||||||
atTop = false
|
atTop = false
|
||||||
case let .known(value):
|
case let .known(value):
|
||||||
atTop = value <= 0.0
|
atTop = value <= 0.0
|
||||||
if startedScrollingAtUpperBound && strongSelf.isTracking {
|
if startedScrollingAtUpperBound && startedScrollingWithCanExpandHiddenItems && strongSelf.isTracking {
|
||||||
revealHiddenItems = value <= -60.0
|
revealHiddenItems = value <= -60.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,12 @@ swift_library(
|
|||||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||||
"//submodules/QrCodeUI:QrCodeUI",
|
"//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 = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -718,7 +718,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
private var indexSections: [String]?
|
private var indexSections: [String]?
|
||||||
|
|
||||||
private var queuedTransitions: [ContactsListNodeTransition] = []
|
private var queuedTransitions: [ContactsListNodeTransition] = []
|
||||||
private var validLayout: (ContainerViewLayout, UIEdgeInsets)?
|
private var validLayout: (ContainerViewLayout, UIEdgeInsets, CGFloat)?
|
||||||
|
|
||||||
private var _ready = ValuePromise<Bool>()
|
private var _ready = ValuePromise<Bool>()
|
||||||
public var ready: Signal<Bool, NoError> {
|
public var ready: Signal<Bool, NoError> {
|
||||||
@ -1369,8 +1369,8 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if let (validLayout, headerInsets) = strongSelf.validLayout {
|
if let (validLayout, headerInsets, storiesInset) = strongSelf.validLayout {
|
||||||
strongSelf.containerLayoutUpdated(validLayout, headerInsets: headerInsets, transition: .immediate)
|
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
|
let hadValidLayout = self.validLayout != nil
|
||||||
self.validLayout = (layout, headerInsets)
|
self.validLayout = (layout, headerInsets, storiesInset)
|
||||||
|
|
||||||
var insets = layout.insets(options: [.input])
|
var insets = layout.insets(options: [.input])
|
||||||
insets.left = layout.safeInsets.left
|
insets.left = layout.safeInsets.left
|
||||||
@ -1446,12 +1449,22 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
headerInsets.top -= navigationBarSearchContentHeight
|
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))
|
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 (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, headerInsets: headerInsets, duration: duration, curve: curve)
|
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 {
|
if let indexSections = self.indexSections {
|
||||||
var insets = layout.insets(options: [.input])
|
var insets = layout.insets(options: [.input])
|
||||||
if let inputHeight = layout.inputHeight {
|
if let inputHeight = layout.inputHeight {
|
||||||
@ -1506,7 +1519,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
options.insert(.AnimateCrossfade)
|
options.insert(.AnimateCrossfade)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let (layout, _) = self.validLayout {
|
if let (layout, _, _) = self.validLayout {
|
||||||
self.indexSections = transition.indexSections
|
self.indexSections = transition.indexSections
|
||||||
|
|
||||||
var insets = layout.insets(options: [.input])
|
var insets = layout.insets(options: [.input])
|
||||||
|
@ -19,18 +19,21 @@ import AppBundle
|
|||||||
import StickerResources
|
import StickerResources
|
||||||
import ContextUI
|
import ContextUI
|
||||||
import QrCodeUI
|
import QrCodeUI
|
||||||
|
import StoryContainerScreen
|
||||||
|
import StoryContentComponent
|
||||||
|
import ChatListHeaderComponent
|
||||||
|
|
||||||
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
||||||
private let controller: ViewController
|
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.controller = controller
|
||||||
self.sourceNode = sourceNode
|
self.sourceView = sourceView
|
||||||
}
|
}
|
||||||
|
|
||||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
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 {
|
public class ContactsController: ViewController {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
|
||||||
@ -159,8 +120,6 @@ public class ContactsController: ViewController {
|
|||||||
private let sortOrderPromise = Promise<ContactsSortOrder>()
|
private let sortOrderPromise = Promise<ContactsSortOrder>()
|
||||||
private let isInVoiceOver = ValuePromise<Bool>(false)
|
private let isInVoiceOver = ValuePromise<Bool>(false)
|
||||||
|
|
||||||
private var searchContentNode: NavigationBarSearchContentNode?
|
|
||||||
|
|
||||||
public var switchToChatsController: (() -> Void)?
|
public var switchToChatsController: (() -> Void)?
|
||||||
|
|
||||||
public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) {
|
public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
@ -178,7 +137,8 @@ public class ContactsController: ViewController {
|
|||||||
|
|
||||||
self.sortButton = SortHeaderButton(presentationData: self.presentationData)
|
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
|
self.tabBarItemContextActionType = .always
|
||||||
|
|
||||||
@ -209,9 +169,6 @@ public class ContactsController: ViewController {
|
|||||||
|
|
||||||
self.scrollToTop = { [weak self] in
|
self.scrollToTop = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let searchContentNode = strongSelf.searchContentNode {
|
|
||||||
searchContentNode.updateExpansionProgress(1.0, animated: true)
|
|
||||||
}
|
|
||||||
strongSelf.contactsNode.scrollToTop()
|
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)
|
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.sortButton.update(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
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.title = self.presentationData.strings.Contacts_Title
|
||||||
self.tabBarItem.title = self.presentationData.strings.Contacts_Title
|
self.tabBarItem.title = self.presentationData.strings.Contacts_Title
|
||||||
if !self.presentationData.reduceMotion {
|
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.displayNode = ContactsControllerNode(context: self.context, sortOrder: sortOrderSignal |> distinctUntilChanged, present: { [weak self] c, a in
|
||||||
self?.present(c, in: .window(.root), with: a)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
}, controller: self)
|
}, 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
|
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.sortButton.contextAction = { [weak self] sourceNode, gesture in
|
||||||
self?.presentSortMenu(sourceNode: sourceNode, gesture: gesture)
|
self?.presentSortMenu(sourceView: sourceNode.view, gesture: gesture)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
@ -533,6 +474,7 @@ public class ContactsController: ViewController {
|
|||||||
override public func viewWillAppear(_ animated: Bool) {
|
override public func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
self.contactsNode.didAppear = true
|
||||||
self.contactsNode.contactListNode.enableUpdates = true
|
self.contactsNode.contactListNode.enableUpdates = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,6 +484,22 @@ public class ContactsController: ViewController {
|
|||||||
self.contactsNode.contactListNode.enableUpdates = false
|
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) {
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
super.containerLayoutUpdated(layout, transition: transition)
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
@ -550,6 +508,124 @@ public class ContactsController: ViewController {
|
|||||||
self.validLayout = layout
|
self.validLayout = layout
|
||||||
|
|
||||||
self.contactsNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, actualNavigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
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() {
|
@objc private func sortPressed() {
|
||||||
@ -557,24 +633,20 @@ public class ContactsController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func activateSearch() {
|
private func activateSearch() {
|
||||||
if self.displayNavigationBar {
|
if let searchContentNode = self.searchContentNode() {
|
||||||
if let searchContentNode = self.searchContentNode {
|
|
||||||
self.contactsNode.activateSearch(placeholderNode: searchContentNode.placeholderNode)
|
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) {
|
private func deactivateSearch(animated: Bool) {
|
||||||
if !self.displayNavigationBar {
|
if let searchContentNode = self.searchContentNode() {
|
||||||
self.setDisplayNavigationBar(true, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate)
|
|
||||||
if let searchContentNode = self.searchContentNode {
|
|
||||||
self.contactsNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated)
|
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
|
let updateSortOrder: (ContactsSortOrder) -> Void = { [weak self] sortOrder in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.sortOrderPromise.set(.single(sortOrder))
|
strongSelf.sortOrderPromise.set(.single(sortOrder))
|
||||||
@ -609,7 +681,7 @@ public class ContactsController: ViewController {
|
|||||||
})))
|
})))
|
||||||
return items
|
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)
|
self.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,3 +780,26 @@ private final class ContactsTabBarContextExtractedContentSource: ContextExtracte
|
|||||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
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 SearchUI
|
||||||
import AppBundle
|
import AppBundle
|
||||||
import ContextUI
|
import ContextUI
|
||||||
|
import ChatListHeaderComponent
|
||||||
|
import ChatListTitleView
|
||||||
|
import ComponentFlow
|
||||||
|
|
||||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||||
let controller: ViewController
|
let controller: ViewController
|
||||||
@ -46,10 +49,13 @@ final class ContactsControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private(set) var searchDisplayController: SearchDisplayController?
|
private(set) var searchDisplayController: SearchDisplayController?
|
||||||
|
private var isSearchDisplayControllerActive: Bool = false
|
||||||
|
private var storiesUnlocked: Bool = false
|
||||||
|
|
||||||
private var containerLayout: (ContainerViewLayout, CGFloat)?
|
private var containerLayout: (ContainerViewLayout, CGFloat)?
|
||||||
|
|
||||||
var navigationBar: NavigationBar?
|
var navigationBar: NavigationBar?
|
||||||
|
let navigationBarView = ComponentView<Empty>()
|
||||||
|
|
||||||
var requestDeactivateSearch: (() -> Void)?
|
var requestDeactivateSearch: (() -> Void)?
|
||||||
var requestOpenPeerFromSearch: ((ContactListPeer) -> Void)?
|
var requestOpenPeerFromSearch: ((ContactListPeer) -> Void)?
|
||||||
@ -64,6 +70,18 @@ final class ContactsControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
weak var controller: ContactsController?
|
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) {
|
init(context: AccountContext, sortOrder: Signal<ContactsSortOrder, NoError>, present: @escaping (ViewController, Any?) -> Void, controller: ContactsController) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
@ -139,10 +157,127 @@ final class ContactsControllerNode: ASDisplayNode {
|
|||||||
contextAction = { [weak self] peer, node, gesture, location in
|
contextAction = { [weak self] peer, node, gesture, location in
|
||||||
self?.contextAction(peer: peer, node: node, gesture: gesture, location: location)
|
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 {
|
deinit {
|
||||||
self.presentationDataDisposable?.dispose()
|
self.presentationDataDisposable?.dispose()
|
||||||
|
self.storySubscriptionsDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateThemeAndStrings() {
|
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) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, actualNavigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
self.containerLayout = (layout, navigationBarHeight)
|
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])
|
var insets = layout.insets(options: [.input])
|
||||||
insets.top += navigationBarHeight
|
insets.top += navigationBarLayout.navigationHeight
|
||||||
|
|
||||||
var headerInsets = layout.insets(options: [.input])
|
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 {
|
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.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?) {
|
private func contextAction(peer: EnginePeer, node: ASDisplayNode?, gesture: ContextGesture?, location: CGPoint?) {
|
||||||
@ -187,11 +480,14 @@ final class ContactsControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode) {
|
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
|
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 {
|
if let requestAddContact = self?.requestAddContact {
|
||||||
requestAddContact(phoneNumber)
|
requestAddContact(phoneNumber)
|
||||||
}
|
}
|
||||||
@ -208,21 +504,29 @@ final class ContactsControllerNode: ASDisplayNode {
|
|||||||
})
|
})
|
||||||
|
|
||||||
self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||||
self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in
|
self.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in
|
||||||
if let strongSelf = self, let strongPlaceholderNode = placeholderNode {
|
if let strongSelf = self {
|
||||||
if isSearchBar {
|
if isSearchBar {
|
||||||
strongPlaceholderNode.supernode?.insertSubnode(subnode, aboveSubnode: strongPlaceholderNode)
|
if let navigationBarComponentView = strongSelf.navigationBarView.view as? ChatListNavigationBar.View {
|
||||||
|
navigationBarComponentView.addSubnode(subnode)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
strongSelf.insertSubnode(subnode, belowSubnode: navigationBar)
|
strongSelf.insertSubnode(subnode, aboveSubnode: strongSelf.contactListNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, placeholder: placeholderNode)
|
}, placeholder: placeholderNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) {
|
func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) {
|
||||||
|
self.isSearchDisplayControllerActive = false
|
||||||
if let searchDisplayController = self.searchDisplayController {
|
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)
|
searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated)
|
||||||
self.searchDisplayController = nil
|
self.searchDisplayController = nil
|
||||||
|
|
||||||
|
placeholderNode.frame = previousFrame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ private enum InviteContactsEntry: Comparable, Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
status = .none
|
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
|
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)
|
interaction.toggleContact(id)
|
||||||
})
|
})
|
||||||
|
@ -1412,7 +1412,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}, openUrl: { _ in }, openPeer: { _ in
|
}, openUrl: { _ in }, openPeer: { _ in
|
||||||
}, showAll: false)
|
}, 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 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)
|
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 {
|
if requestsState.importers.isEmpty && requestsState.isLoadingMore {
|
||||||
count = min(4, state.count)
|
count = min(4, state.count)
|
||||||
loading = true
|
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 {
|
for i in 0 ..< count {
|
||||||
entries.append(.request(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, true))
|
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 {
|
if state.importers.isEmpty && state.isLoadingMore {
|
||||||
count = min(4, state.count)
|
count = min(4, state.count)
|
||||||
loading = true
|
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 {
|
for i in 0 ..< count {
|
||||||
entries.append(.importer(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, false, true))
|
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))
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1))
|
||||||
var peers = SimpleDictionary<PeerId, Peer>()
|
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
|
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
|
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 !selecting {
|
||||||
if let _ = peer {
|
if let _ = peer {
|
||||||
|
@ -1256,24 +1256,29 @@ public final class Transaction {
|
|||||||
return self.postbox!.removeChatHidden(peerId: peerId, index: index)
|
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)
|
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)
|
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)
|
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)
|
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? {
|
public func getLocalStoryState() -> CodableEntry? {
|
||||||
@ -1767,7 +1772,7 @@ final class PostboxImpl {
|
|||||||
self.deviceContactImportInfoTable = DeviceContactImportInfoTable(valueBox: self.valueBox, table: DeviceContactImportInfoTable.tableSpec(54), useCaches: useCaches)
|
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.groupMessageStatsTable = GroupMessageStatsTable(valueBox: self.valueBox, table: GroupMessageStatsTable.tableSpec(58), useCaches: useCaches)
|
||||||
self.storyStatesTable = StoryStatesTable(valueBox: self.valueBox, table: StoryStatesTable.tableSpec(65), 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.storyItemsTable = StoryItemsTable(valueBox: self.valueBox, table: StoryItemsTable.tableSpec(69), useCaches: useCaches)
|
||||||
self.storyTable = StoryTable(valueBox: self.valueBox, table: StoryTable.tableSpec(70), 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)
|
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 (
|
return (
|
||||||
self.storyStatesTable.get(key: .subscriptions),
|
self.storyStatesTable.get(key: .subscriptions(key)),
|
||||||
self.storySubscriptionsTable.getAll()
|
self.storySubscriptionsTable.getAll(subscriptionsKey: key)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func replaceAllStorySubscriptions(state: CodableEntry?, peerIds: [PeerId]) {
|
fileprivate func storySubscriptionsContains(key: PostboxStorySubscriptionsKey, peerId: PeerId) -> Bool {
|
||||||
self.storyStatesTable.set(key: .subscriptions, value: state, events: &self.currentStoryStatesEvents)
|
return self.storySubscriptionsTable.contains(subscriptionsKey: key, peerId: peerId)
|
||||||
self.storySubscriptionsTable.replaceAll(peerIds: peerIds, events: &self.currentStorySubscriptionsEvents)
|
}
|
||||||
|
|
||||||
|
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? {
|
fileprivate func getLocalStoryState() -> CodableEntry? {
|
||||||
return self.storyStatesTable.get(key: .local)
|
return self.storyStatesTable.get(key: .local)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func getSubscriptionsStoriesState() -> CodableEntry? {
|
fileprivate func getSubscriptionsStoriesState(key: PostboxStorySubscriptionsKey) -> CodableEntry? {
|
||||||
return self.storyStatesTable.get(key: .subscriptions)
|
return self.storyStatesTable.get(key: .subscriptions(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func setSubscriptionsStoriesState(state: CodableEntry?) {
|
fileprivate func setSubscriptionsStoriesState(key: PostboxStorySubscriptionsKey, state: CodableEntry?) {
|
||||||
self.storyStatesTable.set(key: .subscriptions, value: state, events: &self.currentStoryStatesEvents)
|
self.storyStatesTable.set(key: .subscriptions(key), value: state, events: &self.currentStoryStatesEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func setLocalStoryState(state: CodableEntry?) {
|
fileprivate func setLocalStoryState(state: CodableEntry?) {
|
||||||
|
@ -7,15 +7,21 @@ final class StoryStatesTable: Table {
|
|||||||
|
|
||||||
enum Key: Hashable {
|
enum Key: Hashable {
|
||||||
case local
|
case local
|
||||||
case subscriptions
|
case subscriptions(PostboxStorySubscriptionsKey)
|
||||||
case peer(PeerId)
|
case peer(PeerId)
|
||||||
|
|
||||||
init(key: ValueBoxKey) {
|
init?(key: ValueBoxKey) {
|
||||||
switch key.getUInt8(0) {
|
switch key.getUInt8(0) {
|
||||||
case 0:
|
case 0:
|
||||||
self = .local
|
self = .local
|
||||||
case 1:
|
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:
|
case 2:
|
||||||
self = .peer(PeerId(key.getInt64(1)))
|
self = .peer(PeerId(key.getInt64(1)))
|
||||||
default:
|
default:
|
||||||
@ -30,9 +36,10 @@ final class StoryStatesTable: Table {
|
|||||||
let key = ValueBoxKey(length: 1)
|
let key = ValueBoxKey(length: 1)
|
||||||
key.setUInt8(0, value: 0)
|
key.setUInt8(0, value: 0)
|
||||||
return key
|
return key
|
||||||
case .subscriptions:
|
case let .subscriptions(subscriptionsKey):
|
||||||
let key = ValueBoxKey(length: 1)
|
let key = ValueBoxKey(length: 1 + 4)
|
||||||
key.setUInt8(0, value: 1)
|
key.setUInt8(0, value: 1)
|
||||||
|
key.setInt32(1, value: subscriptionsKey.rawValue)
|
||||||
return key
|
return key
|
||||||
case let .peer(peerId):
|
case let .peer(peerId):
|
||||||
let key = ValueBoxKey(length: 1 + 8)
|
let key = ValueBoxKey(length: 1 + 8)
|
||||||
|
@ -2,7 +2,7 @@ import Foundation
|
|||||||
|
|
||||||
public enum PostboxStoryStatesKey: Hashable {
|
public enum PostboxStoryStatesKey: Hashable {
|
||||||
case local
|
case local
|
||||||
case subscriptions
|
case subscriptions(PostboxStorySubscriptionsKey)
|
||||||
case peer(PeerId)
|
case peer(PeerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,8 +11,8 @@ private extension PostboxStoryStatesKey {
|
|||||||
switch tableKey {
|
switch tableKey {
|
||||||
case .local:
|
case .local:
|
||||||
self = .local
|
self = .local
|
||||||
case .subscriptions:
|
case let .subscriptions(key):
|
||||||
self = .subscriptions
|
self = .subscriptions(key)
|
||||||
case let .peer(peerId):
|
case let .peer(peerId):
|
||||||
self = .peer(peerId)
|
self = .peer(peerId)
|
||||||
}
|
}
|
||||||
@ -22,8 +22,8 @@ private extension PostboxStoryStatesKey {
|
|||||||
switch self {
|
switch self {
|
||||||
case .local:
|
case .local:
|
||||||
return .local
|
return .local
|
||||||
case .subscriptions:
|
case let .subscriptions(key):
|
||||||
return .subscriptions
|
return .subscriptions(key)
|
||||||
case let .peer(peerId):
|
case let .peer(peerId):
|
||||||
return .peer(peerId)
|
return .peer(peerId)
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,11 @@ import Foundation
|
|||||||
|
|
||||||
final class StorySubscriptionsTable: Table {
|
final class StorySubscriptionsTable: Table {
|
||||||
enum Event {
|
enum Event {
|
||||||
case replaceAll
|
case replaceAll(key: PostboxStorySubscriptionsKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct Key: Hashable {
|
private struct Key: Hashable {
|
||||||
|
var subscriptionsKey: PostboxStorySubscriptionsKey
|
||||||
var peerId: PeerId
|
var peerId: PeerId
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,20 +14,27 @@ final class StorySubscriptionsTable: Table {
|
|||||||
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false)
|
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 {
|
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
|
return self.sharedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getAllKeys() -> [Key] {
|
private func getAllKeys(subscriptionsKey: PostboxStorySubscriptionsKey) -> [Key] {
|
||||||
var result: [Key] = []
|
var result: [Key] = []
|
||||||
|
|
||||||
self.valueBox.scan(self.table, keys: { key in
|
self.valueBox.scan(self.table, keys: { key in
|
||||||
let peerId = PeerId(key.getInt64(0))
|
if key.length != 4 + 8 {
|
||||||
|
return true
|
||||||
result.append(Key(peerId: peerId))
|
}
|
||||||
|
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
|
return true
|
||||||
})
|
})
|
||||||
@ -34,12 +42,19 @@ final class StorySubscriptionsTable: Table {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getAll() -> [PeerId] {
|
public func getAll(subscriptionsKey: PostboxStorySubscriptionsKey) -> [PeerId] {
|
||||||
var result: [PeerId] = []
|
var result: [PeerId] = []
|
||||||
|
|
||||||
self.valueBox.scan(self.table, keys: { key in
|
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)
|
result.append(peerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@ -47,16 +62,24 @@ final class StorySubscriptionsTable: Table {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
public func replaceAll(peerIds: [PeerId], events: inout [Event]) {
|
public func contains(subscriptionsKey: PostboxStorySubscriptionsKey, peerId: PeerId) -> Bool {
|
||||||
for key in self.getAllKeys() {
|
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)
|
self.valueBox.remove(self.table, key: self.key(key), secure: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
for peerId in peerIds {
|
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() {
|
override func clearMemoryCache() {
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
public enum PostboxStorySubscriptionsKey: Int32 {
|
||||||
|
case all = 0
|
||||||
|
case filtered = 1
|
||||||
|
}
|
||||||
|
|
||||||
final class MutableStorySubscriptionsView: MutablePostboxView {
|
final class MutableStorySubscriptionsView: MutablePostboxView {
|
||||||
|
private let key: PostboxStorySubscriptionsKey
|
||||||
var peerIds: [PeerId]
|
var peerIds: [PeerId]
|
||||||
|
|
||||||
init(postbox: PostboxImpl) {
|
init(postbox: PostboxImpl, key: PostboxStorySubscriptionsKey) {
|
||||||
self.peerIds = postbox.storySubscriptionsTable.getAll()
|
self.key = key
|
||||||
|
self.peerIds = postbox.storySubscriptionsTable.getAll(subscriptionsKey: self.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
|
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
|
||||||
@ -12,8 +19,9 @@ final class MutableStorySubscriptionsView: MutablePostboxView {
|
|||||||
if !transaction.storySubscriptionsEvents.isEmpty {
|
if !transaction.storySubscriptionsEvents.isEmpty {
|
||||||
loop: for event in transaction.storySubscriptionsEvents {
|
loop: for event in transaction.storySubscriptionsEvents {
|
||||||
switch event {
|
switch event {
|
||||||
case .replaceAll:
|
case let .replaceAll(key):
|
||||||
let peerIds = postbox.storySubscriptionsTable.getAll()
|
if key == self.key {
|
||||||
|
let peerIds = postbox.storySubscriptionsTable.getAll(subscriptionsKey: self.key)
|
||||||
if self.peerIds != peerIds {
|
if self.peerIds != peerIds {
|
||||||
updated = true
|
updated = true
|
||||||
self.peerIds = peerIds
|
self.peerIds = peerIds
|
||||||
@ -23,11 +31,12 @@ final class MutableStorySubscriptionsView: MutablePostboxView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
let peerIds = postbox.storySubscriptionsTable.getAll()
|
let peerIds = postbox.storySubscriptionsTable.getAll(subscriptionsKey: self.key)
|
||||||
if self.peerIds != peerIds {
|
if self.peerIds != peerIds {
|
||||||
self.peerIds = peerIds
|
self.peerIds = peerIds
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ public enum PostboxViewKey: Hashable {
|
|||||||
case peerTimeoutAttributes
|
case peerTimeoutAttributes
|
||||||
case messageHistoryThreadIndex(id: PeerId, summaryComponents: ChatListEntrySummaryComponents)
|
case messageHistoryThreadIndex(id: PeerId, summaryComponents: ChatListEntrySummaryComponents)
|
||||||
case messageHistoryThreadInfo(peerId: PeerId, threadId: Int64)
|
case messageHistoryThreadInfo(peerId: PeerId, threadId: Int64)
|
||||||
case storySubscriptions
|
case storySubscriptions(key: PostboxStorySubscriptionsKey)
|
||||||
case storiesState(key: PostboxStoryStatesKey)
|
case storiesState(key: PostboxStoryStatesKey)
|
||||||
case storyItems(peerId: PeerId)
|
case storyItems(peerId: PeerId)
|
||||||
|
|
||||||
@ -137,8 +137,9 @@ public enum PostboxViewKey: Hashable {
|
|||||||
case let .messageHistoryThreadInfo(peerId, threadId):
|
case let .messageHistoryThreadInfo(peerId, threadId):
|
||||||
hasher.combine(peerId)
|
hasher.combine(peerId)
|
||||||
hasher.combine(threadId)
|
hasher.combine(threadId)
|
||||||
case .storySubscriptions:
|
case let .storySubscriptions(key):
|
||||||
hasher.combine(18)
|
hasher.combine(18)
|
||||||
|
hasher.combine(key)
|
||||||
case let .storiesState(key):
|
case let .storiesState(key):
|
||||||
hasher.combine(key)
|
hasher.combine(key)
|
||||||
case let .storyItems(peerId):
|
case let .storyItems(peerId):
|
||||||
@ -382,8 +383,8 @@ public enum PostboxViewKey: Hashable {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case .storySubscriptions:
|
case let .storySubscriptions(key):
|
||||||
if case .storySubscriptions = rhs {
|
if case .storySubscriptions(key) = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -484,8 +485,8 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost
|
|||||||
return MutableMessageHistoryThreadIndexView(postbox: postbox, peerId: id, summaryComponents: summaryComponents)
|
return MutableMessageHistoryThreadIndexView(postbox: postbox, peerId: id, summaryComponents: summaryComponents)
|
||||||
case let .messageHistoryThreadInfo(peerId, threadId):
|
case let .messageHistoryThreadInfo(peerId, threadId):
|
||||||
return MutableMessageHistoryThreadInfoView(postbox: postbox, peerId: peerId, threadId: threadId)
|
return MutableMessageHistoryThreadInfoView(postbox: postbox, peerId: peerId, threadId: threadId)
|
||||||
case .storySubscriptions:
|
case let .storySubscriptions(key):
|
||||||
return MutableStorySubscriptionsView(postbox: postbox)
|
return MutableStorySubscriptionsView(postbox: postbox, key: key)
|
||||||
case let .storiesState(key):
|
case let .storiesState(key):
|
||||||
return MutableStoryStatesView(postbox: postbox, key: key)
|
return MutableStoryStatesView(postbox: postbox, key: key)
|
||||||
case let .storyItems(peerId):
|
case let .storyItems(peerId):
|
||||||
|
@ -163,8 +163,8 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
|||||||
let otherPeerId = self.context.account.peerId
|
let otherPeerId = self.context.account.peerId
|
||||||
var peers = SimpleDictionary<PeerId, Peer>()
|
var peers = SimpleDictionary<PeerId, Peer>()
|
||||||
var messages = SimpleDictionary<MessageId, Message>()
|
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[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: [])
|
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)
|
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: [:])
|
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>()
|
var peers = SimpleDictionary<PeerId, Peer>()
|
||||||
let messages = SimpleDictionary<MessageId, Message>()
|
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: [])
|
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>()
|
var peers = SimpleDictionary<PeerId, Peer>()
|
||||||
let messages = SimpleDictionary<MessageId, Message>()
|
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
|
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 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: []))
|
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: []))
|
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 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 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: []))
|
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 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
|
let timestamp = self.referenceTimestamp
|
||||||
|
|
||||||
@ -425,8 +425,8 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
|||||||
let otherPeerId = self.context.account.peerId
|
let otherPeerId = self.context.account.peerId
|
||||||
var peers = SimpleDictionary<PeerId, Peer>()
|
var peers = SimpleDictionary<PeerId, Peer>()
|
||||||
var messages = SimpleDictionary<MessageId, Message>()
|
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[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: [])
|
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)
|
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: [:])
|
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 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: []))
|
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: []))
|
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 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 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: []))
|
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
|
let timestamp = self.referenceTimestamp
|
||||||
|
|
||||||
@ -1028,8 +1028,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
|||||||
let otherPeerId = self.context.account.peerId
|
let otherPeerId = self.context.account.peerId
|
||||||
var peers = SimpleDictionary<PeerId, Peer>()
|
var peers = SimpleDictionary<PeerId, Peer>()
|
||||||
var messages = SimpleDictionary<MessageId, Message>()
|
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[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: [])
|
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] = []
|
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 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 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: []))
|
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: []))
|
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 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 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: []))
|
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 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 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 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
|
let timestamp = self.referenceTimestamp
|
||||||
|
|
||||||
@ -579,8 +579,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
let otherPeerId = self.context.account.peerId
|
let otherPeerId = self.context.account.peerId
|
||||||
var peers = SimpleDictionary<PeerId, Peer>()
|
var peers = SimpleDictionary<PeerId, Peer>()
|
||||||
var messages = SimpleDictionary<MessageId, Message>()
|
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[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: [])
|
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] = []
|
var sampleMessages: [Message] = []
|
||||||
|
|
||||||
|
@ -155,11 +155,11 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
|
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
|
||||||
if let (author, text) = messageItem.reply {
|
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: [:])
|
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))
|
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
|
let otherPeerId = self.context.account.peerId
|
||||||
var peers = SimpleDictionary<PeerId, Peer>()
|
var peers = SimpleDictionary<PeerId, Peer>()
|
||||||
let messages = SimpleDictionary<MessageId, Message>()
|
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[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: [])
|
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 topMessageText = ""
|
||||||
var bottomMessageText = ""
|
var bottomMessageText = ""
|
||||||
|
@ -973,7 +973,8 @@ public class Account {
|
|||||||
|
|
||||||
let networkStatsContext: NetworkStatsContext
|
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) {
|
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
|
self.accountManager = accountManager
|
||||||
@ -993,9 +994,11 @@ public class Account {
|
|||||||
self.peerInputActivityManager = PeerInputActivityManager()
|
self.peerInputActivityManager = PeerInputActivityManager()
|
||||||
|
|
||||||
if !supplementary {
|
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 {
|
} 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
|
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)
|
userFlags.insert(.isCloseFriend)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var storiesHidden: Bool?
|
||||||
|
if !isMin {
|
||||||
|
storiesHidden = (flags2 & (1 << 5)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
var botInfo: BotUserInfo?
|
var botInfo: BotUserInfo?
|
||||||
if (flags & (1 << 14)) != 0 {
|
if (flags & (1 << 14)) != 0 {
|
||||||
var botFlags = BotUserInfoFlags()
|
var botFlags = BotUserInfoFlags()
|
||||||
@ -91,9 +96,9 @@ extension TelegramUser {
|
|||||||
|
|
||||||
let restrictionInfo: PeerAccessRestrictionInfo? = restrictionReason.flatMap(PeerAccessRestrictionInfo.init(apiReasons:))
|
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):
|
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
|
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 {
|
} else {
|
||||||
return TelegramUser(user: rhs)
|
return TelegramUser(user: rhs)
|
||||||
}
|
}
|
||||||
@ -228,7 +233,14 @@ extension TelegramUser {
|
|||||||
photo = rhs.photo
|
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 {
|
if updatedState.peers[peerId] == nil {
|
||||||
updatedState.updatePeer(peerId, { peer in
|
updatedState.updatePeer(peerId, { peer in
|
||||||
if peer == nil {
|
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 {
|
} else {
|
||||||
return peer
|
return peer
|
||||||
}
|
}
|
||||||
@ -4351,9 +4351,9 @@ func replayFinalState(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscriptionsOpaqueState: String?
|
var filteredSubscriptionsOpaqueState: String?
|
||||||
if let state = transaction.getSubscriptionsStoriesState()?.get(Stories.SubscriptionsState.self) {
|
if let state = transaction.getSubscriptionsStoriesState(key: .filtered)?.get(Stories.SubscriptionsState.self) {
|
||||||
subscriptionsOpaqueState = state.opaqueState
|
filteredSubscriptionsOpaqueState = state.opaqueState
|
||||||
}
|
}
|
||||||
var appliedMaxReadId: Int32?
|
var appliedMaxReadId: Int32?
|
||||||
if let currentState = transaction.getPeerStoryState(peerId: peerId)?.get(Stories.PeerState.self) {
|
if let currentState = transaction.getPeerStoryState(peerId: peerId)?.get(Stories.PeerState.self) {
|
||||||
@ -4366,7 +4366,7 @@ func replayFinalState(
|
|||||||
|
|
||||||
transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries)
|
transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries)
|
||||||
transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState(
|
transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState(
|
||||||
subscriptionsOpaqueState: subscriptionsOpaqueState,
|
subscriptionsOpaqueState: filteredSubscriptionsOpaqueState,
|
||||||
maxReadId: appliedMaxReadId ?? 0
|
maxReadId: appliedMaxReadId ?? 0
|
||||||
)))
|
)))
|
||||||
|
|
||||||
@ -4381,12 +4381,12 @@ func replayFinalState(
|
|||||||
appliedMaxReadId = max(appliedMaxReadId, currentState.maxReadId)
|
appliedMaxReadId = max(appliedMaxReadId, currentState.maxReadId)
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscriptionsOpaqueState: String?
|
var filteredSubscriptionsOpaqueState: String?
|
||||||
if let state = transaction.getSubscriptionsStoriesState()?.get(Stories.SubscriptionsState.self) {
|
if let state = transaction.getSubscriptionsStoriesState(key: .filtered)?.get(Stories.SubscriptionsState.self) {
|
||||||
subscriptionsOpaqueState = state.opaqueState
|
filteredSubscriptionsOpaqueState = state.opaqueState
|
||||||
}
|
}
|
||||||
transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState(
|
transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState(
|
||||||
subscriptionsOpaqueState: subscriptionsOpaqueState,
|
subscriptionsOpaqueState: filteredSubscriptionsOpaqueState,
|
||||||
maxReadId: appliedMaxReadId
|
maxReadId: appliedMaxReadId
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
@ -109,6 +109,7 @@ public final class TelegramUser: Peer, Equatable {
|
|||||||
public let flags: UserInfoFlags
|
public let flags: UserInfoFlags
|
||||||
public let emojiStatus: PeerEmojiStatus?
|
public let emojiStatus: PeerEmojiStatus?
|
||||||
public let usernames: [TelegramPeerUsername]
|
public let usernames: [TelegramPeerUsername]
|
||||||
|
public let storiesHidden: Bool?
|
||||||
|
|
||||||
public var nameOrPhone: String {
|
public var nameOrPhone: String {
|
||||||
if let firstName = self.firstName {
|
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.id = id
|
||||||
self.accessHash = accessHash
|
self.accessHash = accessHash
|
||||||
self.firstName = firstName
|
self.firstName = firstName
|
||||||
@ -182,6 +183,7 @@ public final class TelegramUser: Peer, Equatable {
|
|||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.emojiStatus = emojiStatus
|
self.emojiStatus = emojiStatus
|
||||||
self.usernames = usernames
|
self.usernames = usernames
|
||||||
|
self.storiesHidden = storiesHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
@ -220,6 +222,7 @@ public final class TelegramUser: Peer, Equatable {
|
|||||||
self.emojiStatus = decoder.decode(PeerEmojiStatus.self, forKey: "emjs")
|
self.emojiStatus = decoder.decode(PeerEmojiStatus.self, forKey: "emjs")
|
||||||
|
|
||||||
self.usernames = decoder.decodeObjectArrayForKey("uns")
|
self.usernames = decoder.decodeObjectArrayForKey("uns")
|
||||||
|
self.storiesHidden = decoder.decodeOptionalBoolForKey("sth")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
@ -273,6 +276,12 @@ public final class TelegramUser: Peer, Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
encoder.encodeObjectArray(self.usernames, forKey: "uns")
|
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 {
|
public func isEqual(_ other: Peer) -> Bool {
|
||||||
@ -325,35 +334,42 @@ public final class TelegramUser: Peer, Equatable {
|
|||||||
if lhs.usernames != rhs.usernames {
|
if lhs.usernames != rhs.usernames {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.storiesHidden != rhs.storiesHidden {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedUsername(_ username: String?) -> TelegramUser {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 queue: Queue
|
||||||
private let postbox: Postbox
|
private let postbox: Postbox
|
||||||
private let network: Network
|
private let network: Network
|
||||||
|
private let includesHidden: Bool
|
||||||
|
|
||||||
private var taskState = TaskState()
|
private var taskState = TaskState()
|
||||||
|
|
||||||
@ -121,11 +122,12 @@ public final class StorySubscriptionsContext {
|
|||||||
private let loadMoreDisposable = MetaDisposable()
|
private let loadMoreDisposable = MetaDisposable()
|
||||||
private let refreshTimerDisposable = 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.accountPeerId = accountPeerId
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.postbox = postbox
|
self.postbox = postbox
|
||||||
self.network = network
|
self.network = network
|
||||||
|
self.includesHidden = includesHidden
|
||||||
|
|
||||||
self.taskState.isRefreshScheduled = true
|
self.taskState.isRefreshScheduled = true
|
||||||
|
|
||||||
@ -148,16 +150,18 @@ public final class StorySubscriptionsContext {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let subscriptionsKey: PostboxStorySubscriptionsKey = self.includesHidden ? .all : .filtered
|
||||||
|
|
||||||
if self.taskState.isRefreshScheduled {
|
if self.taskState.isRefreshScheduled {
|
||||||
self.isLoading = true
|
self.isLoading = true
|
||||||
|
|
||||||
self.stateDisposable = (postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .subscriptions)])
|
self.stateDisposable = (postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))])
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] views in
|
|> deliverOn(self.queue)).start(next: { [weak self] views in
|
||||||
guard let `self` = self else {
|
guard let `self` = self else {
|
||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,13 +177,13 @@ public final class StorySubscriptionsContext {
|
|||||||
} else if self.taskState.isLoadMoreScheduled {
|
} else if self.taskState.isLoadMoreScheduled {
|
||||||
self.isLoading = true
|
self.isLoading = true
|
||||||
|
|
||||||
self.stateDisposable = (postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .subscriptions)])
|
self.stateDisposable = (postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))])
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] views in
|
|> deliverOn(self.queue)).start(next: { [weak self] views in
|
||||||
guard let `self` = self else {
|
guard let `self` = self else {
|
||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +210,11 @@ public final class StorySubscriptionsContext {
|
|||||||
|
|
||||||
private func loadImpl(isRefresh: Bool, stateMark: OpaqueStateMark) {
|
private func loadImpl(isRefresh: Bool, stateMark: OpaqueStateMark) {
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
|
|
||||||
|
if self.includesHidden {
|
||||||
|
flags |= 1 << 2
|
||||||
|
}
|
||||||
|
|
||||||
var state: String?
|
var state: String?
|
||||||
switch stateMark {
|
switch stateMark {
|
||||||
case .empty:
|
case .empty:
|
||||||
@ -228,6 +237,9 @@ public final class StorySubscriptionsContext {
|
|||||||
|
|
||||||
let accountPeerId = self.accountPeerId
|
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))
|
self.loadMoreDisposable.set((self.network.request(Api.functions.stories.getAllStories(flags: flags, state: state))
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] result in
|
|> deliverOn(self.queue)).start(next: { [weak self] result in
|
||||||
guard let `self` = self else {
|
guard let `self` = self else {
|
||||||
@ -238,7 +250,7 @@ public final class StorySubscriptionsContext {
|
|||||||
switch result {
|
switch result {
|
||||||
case let .allStoriesNotModified(state):
|
case let .allStoriesNotModified(state):
|
||||||
self.loadedStateMark = .value(state)
|
self.loadedStateMark = .value(state)
|
||||||
let (currentStateValue, _) = transaction.getAllStorySubscriptions()
|
let (currentStateValue, _) = transaction.getAllStorySubscriptions(key: subscriptionsKey)
|
||||||
let currentState = currentStateValue.flatMap { $0.get(Stories.SubscriptionsState.self) }
|
let currentState = currentStateValue.flatMap { $0.get(Stories.SubscriptionsState.self) }
|
||||||
|
|
||||||
var hasMore = false
|
var hasMore = false
|
||||||
@ -246,7 +258,7 @@ public final class StorySubscriptionsContext {
|
|||||||
hasMore = currentState.hasMore
|
hasMore = currentState.hasMore
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction.setSubscriptionsStoriesState(state: CodableEntry(Stories.SubscriptionsState(
|
transaction.setSubscriptionsStoriesState(key: subscriptionsKey, state: CodableEntry(Stories.SubscriptionsState(
|
||||||
opaqueState: state,
|
opaqueState: state,
|
||||||
refreshId: currentState?.refreshId ?? UInt64.random(in: 0 ... UInt64.max),
|
refreshId: currentState?.refreshId ?? UInt64.random(in: 0 ... UInt64.max),
|
||||||
hasMore: hasMore
|
hasMore: hasMore
|
||||||
@ -263,14 +275,9 @@ public final class StorySubscriptionsContext {
|
|||||||
peerPresences[telegramUser.id] = user
|
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 hasMore: Bool = (flags & (1 << 0)) != 0
|
||||||
|
|
||||||
let (_, currentPeerItems) = transaction.getAllStorySubscriptions()
|
let (_, currentPeerItems) = transaction.getAllStorySubscriptions(key: subscriptionsKey)
|
||||||
var peerEntries: [PeerId] = []
|
var peerEntries: [PeerId] = []
|
||||||
|
|
||||||
for userStorySet in userStories {
|
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) })
|
let leftPeerIds = currentPeerItems.filter({ !peerEntries.contains($0) })
|
||||||
if !leftPeerIds.isEmpty {
|
if !leftPeerIds.isEmpty {
|
||||||
peerEntries = leftPeerIds + peerEntries
|
peerEntries = leftPeerIds + peerEntries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction.replaceAllStorySubscriptions(state: CodableEntry(Stories.SubscriptionsState(
|
transaction.replaceAllStorySubscriptions(key: subscriptionsKey, state: CodableEntry(Stories.SubscriptionsState(
|
||||||
opaqueState: state,
|
opaqueState: state,
|
||||||
refreshId: UInt64.random(in: 0 ... UInt64.max),
|
refreshId: UInt64.random(in: 0 ... UInt64.max),
|
||||||
hasMore: hasMore
|
hasMore: hasMore
|
||||||
)), peerIds: peerEntries)
|
)), 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
|
|> deliverOn(self.queue)).start(completed: { [weak self] in
|
||||||
@ -355,10 +373,10 @@ public final class StorySubscriptionsContext {
|
|||||||
private let queue = Queue(name: "StorySubscriptionsContext")
|
private let queue = Queue(name: "StorySubscriptionsContext")
|
||||||
private let impl: QueueLocalObject<Impl>
|
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
|
let queue = self.queue
|
||||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
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()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func storySubscriptions() -> Signal<EngineStorySubscriptions, NoError> {
|
public func storySubscriptions(includeHidden: Bool) -> Signal<EngineStorySubscriptions, NoError> {
|
||||||
let debugTimerSignal: Signal<Bool, NoError>
|
let debugTimerSignal: Signal<Bool, NoError>
|
||||||
#if DEBUG && false
|
#if DEBUG && false
|
||||||
debugTimerSignal = Signal<Bool, NoError>.single(true)
|
debugTimerSignal = Signal<Bool, NoError>.single(true)
|
||||||
@ -609,21 +609,24 @@ public extension TelegramEngine {
|
|||||||
debugTimerSignal = .single(true)
|
debugTimerSignal = .single(true)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
let subscriptionsKey: PostboxStorySubscriptionsKey = includeHidden ? .all : .filtered
|
||||||
|
|
||||||
let basicPeerKey = PostboxViewKey.basicPeer(self.account.peerId)
|
let basicPeerKey = PostboxViewKey.basicPeer(self.account.peerId)
|
||||||
|
let storySubscriptionsKey = PostboxViewKey.storySubscriptions(key: subscriptionsKey)
|
||||||
return combineLatest(debugTimerSignal |> distinctUntilChanged,
|
return combineLatest(debugTimerSignal |> distinctUntilChanged,
|
||||||
self.account.postbox.combinedView(keys: [
|
self.account.postbox.combinedView(keys: [
|
||||||
basicPeerKey,
|
basicPeerKey,
|
||||||
PostboxViewKey.storySubscriptions,
|
storySubscriptionsKey,
|
||||||
PostboxViewKey.storiesState(key: .subscriptions)
|
PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))
|
||||||
]))
|
]))
|
||||||
|> mapToSignal { debugTimer, views -> Signal<EngineStorySubscriptions, NoError> in
|
|> mapToSignal { debugTimer, views -> Signal<EngineStorySubscriptions, NoError> in
|
||||||
guard let basicPeerView = views.views[basicPeerKey] as? BasicPeerView, let accountPeer = basicPeerView.peer else {
|
guard let basicPeerView = views.views[basicPeerKey] as? BasicPeerView, let accountPeer = basicPeerView.peer else {
|
||||||
return .single(EngineStorySubscriptions(accountItem: nil, items: [], hasMoreToken: nil))
|
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))
|
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))
|
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 basicPeerKey = PostboxViewKey.basicPeer(self.account.peerId)
|
||||||
|
let subscriptionsKey: PostboxStorySubscriptionsKey = includeHidden ? .all : .filtered
|
||||||
|
let storySubscriptionsKey = PostboxViewKey.storySubscriptions(key: subscriptionsKey)
|
||||||
return self.account.postbox.combinedView(keys: [
|
return self.account.postbox.combinedView(keys: [
|
||||||
basicPeerKey,
|
basicPeerKey,
|
||||||
PostboxViewKey.storySubscriptions,
|
storySubscriptionsKey,
|
||||||
PostboxViewKey.storiesState(key: .subscriptions)
|
PostboxViewKey.storiesState(key: .subscriptions(subscriptionsKey))
|
||||||
])
|
])
|
||||||
|> mapToSignal { views -> Signal<[EngineMediaResource.Id: StoryPreloadInfo], NoError> in
|
|> mapToSignal { views -> Signal<[EngineMediaResource.Id: StoryPreloadInfo], NoError> in
|
||||||
guard let basicPeerView = views.views[basicPeerKey] as? BasicPeerView, let accountPeer = basicPeerView.peer else {
|
guard let basicPeerView = views.views[basicPeerKey] as? BasicPeerView, let accountPeer = basicPeerView.peer else {
|
||||||
return .single([:])
|
return .single([:])
|
||||||
}
|
}
|
||||||
guard let storySubscriptionsView = views.views[PostboxViewKey.storySubscriptions] as? StorySubscriptionsView else {
|
guard let storySubscriptionsView = views.views[storySubscriptionsKey] as? StorySubscriptionsView else {
|
||||||
return .single([:])
|
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([:])
|
return .single([:])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -899,5 +904,9 @@ public extension TelegramEngine {
|
|||||||
public func storyViewList(id: Int32, views: EngineStoryItem.Views) -> EngineStoryViewListContext {
|
public func storyViewList(id: Int32, views: EngineStoryItem.Views) -> EngineStoryViewListContext {
|
||||||
return EngineStoryViewListContext(account: self.account, storyId: id, views: views)
|
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] {
|
public func tokenizeSearchString(string: String, transliteration: EngineStringIndexTokenTransliteration) -> [EngineDataBuffer] {
|
||||||
return stringIndexTokens(string, transliteration: transliteration)
|
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 {
|
switch peerId.namespace {
|
||||||
case Namespaces.Peer.CloudUser:
|
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:
|
case Namespaces.Peer.CloudGroup:
|
||||||
if let group = updated as? TelegramGroup {
|
if let group = updated as? TelegramGroup {
|
||||||
if group.flags.contains(.deactivated) {
|
if group.flags.contains(.deactivated) {
|
||||||
|
@ -133,7 +133,9 @@ public final class ChatListHeaderComponent: Component {
|
|||||||
public let secondaryTransition: CGFloat
|
public let secondaryTransition: CGFloat
|
||||||
public let networkStatus: HeaderNetworkStatusComponent.Content?
|
public let networkStatus: HeaderNetworkStatusComponent.Content?
|
||||||
public let storySubscriptions: EngineStorySubscriptions?
|
public let storySubscriptions: EngineStorySubscriptions?
|
||||||
|
public let storiesIncludeHidden: Bool
|
||||||
public let storiesFraction: CGFloat
|
public let storiesFraction: CGFloat
|
||||||
|
public let storiesUnlockedFraction: CGFloat
|
||||||
public let uploadProgress: Float?
|
public let uploadProgress: Float?
|
||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
@ -149,7 +151,9 @@ public final class ChatListHeaderComponent: Component {
|
|||||||
secondaryTransition: CGFloat,
|
secondaryTransition: CGFloat,
|
||||||
networkStatus: HeaderNetworkStatusComponent.Content?,
|
networkStatus: HeaderNetworkStatusComponent.Content?,
|
||||||
storySubscriptions: EngineStorySubscriptions?,
|
storySubscriptions: EngineStorySubscriptions?,
|
||||||
|
storiesIncludeHidden: Bool,
|
||||||
storiesFraction: CGFloat,
|
storiesFraction: CGFloat,
|
||||||
|
storiesUnlockedFraction: CGFloat,
|
||||||
uploadProgress: Float?,
|
uploadProgress: Float?,
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
@ -164,7 +168,9 @@ public final class ChatListHeaderComponent: Component {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.networkStatus = networkStatus
|
self.networkStatus = networkStatus
|
||||||
self.storySubscriptions = storySubscriptions
|
self.storySubscriptions = storySubscriptions
|
||||||
|
self.storiesIncludeHidden = storiesIncludeHidden
|
||||||
self.storiesFraction = storiesFraction
|
self.storiesFraction = storiesFraction
|
||||||
|
self.storiesUnlockedFraction = storiesUnlockedFraction
|
||||||
self.uploadProgress = uploadProgress
|
self.uploadProgress = uploadProgress
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
@ -191,9 +197,15 @@ public final class ChatListHeaderComponent: Component {
|
|||||||
if lhs.storySubscriptions != rhs.storySubscriptions {
|
if lhs.storySubscriptions != rhs.storySubscriptions {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.storiesIncludeHidden != rhs.storiesIncludeHidden {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.storiesFraction != rhs.storiesFraction {
|
if lhs.storiesFraction != rhs.storiesFraction {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.storiesUnlockedFraction != rhs.storiesUnlockedFraction {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.uploadProgress != rhs.uploadProgress {
|
if lhs.uploadProgress != rhs.uploadProgress {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -799,12 +811,6 @@ public final class ChatListHeaderComponent: Component {
|
|||||||
var storyListTransition = transition
|
var storyListTransition = transition
|
||||||
|
|
||||||
if let storySubscriptions = component.storySubscriptions {
|
if let storySubscriptions = component.storySubscriptions {
|
||||||
var storyOffsetFraction: CGFloat = 1.0
|
|
||||||
storyOffsetFraction = component.storiesFraction
|
|
||||||
let _ = storyOffsetFraction
|
|
||||||
|
|
||||||
//self.storyOffsetFraction = storyOffsetFraction
|
|
||||||
|
|
||||||
let storyPeerList: ComponentView<Empty>
|
let storyPeerList: ComponentView<Empty>
|
||||||
if let current = self.storyPeerList {
|
if let current = self.storyPeerList {
|
||||||
storyPeerList = current
|
storyPeerList = current
|
||||||
@ -821,8 +827,10 @@ public final class ChatListHeaderComponent: Component {
|
|||||||
context: component.context,
|
context: component.context,
|
||||||
theme: component.theme,
|
theme: component.theme,
|
||||||
strings: component.strings,
|
strings: component.strings,
|
||||||
|
includesHidden: component.storiesIncludeHidden,
|
||||||
storySubscriptions: storySubscriptions,
|
storySubscriptions: storySubscriptions,
|
||||||
collapseFraction: 1.0 - component.storiesFraction,
|
collapseFraction: 1.0 - component.storiesFraction,
|
||||||
|
unlockedFraction: 1.0 - component.storiesUnlockedFraction,
|
||||||
uploadProgress: component.uploadProgress,
|
uploadProgress: component.uploadProgress,
|
||||||
peerAction: { [weak self] peer in
|
peerAction: { [weak self] peer in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -971,8 +979,8 @@ public final class ChatListHeaderComponent: Component {
|
|||||||
self.addSubview(storyPeerListComponentView)
|
self.addSubview(storyPeerListComponentView)
|
||||||
}
|
}
|
||||||
|
|
||||||
let storyPeerListMinOffset: CGFloat = -8.0
|
let storyPeerListMinOffset: CGFloat = -7.0
|
||||||
let storyPeerListMaxOffset: CGFloat = availableSize.height + 2.0
|
let storyPeerListMaxOffset: CGFloat = availableSize.height + 8.0
|
||||||
|
|
||||||
let storyPeerListPosition: CGFloat = storyPeerListMinOffset * (1.0 - component.storiesFraction) + storyPeerListMaxOffset * component.storiesFraction
|
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 secondaryContent: ChatListHeaderComponent.Content?
|
||||||
public let secondaryTransition: CGFloat
|
public let secondaryTransition: CGFloat
|
||||||
public let storySubscriptions: EngineStorySubscriptions?
|
public let storySubscriptions: EngineStorySubscriptions?
|
||||||
|
public let storiesIncludeHidden: Bool
|
||||||
public let uploadProgress: Float?
|
public let uploadProgress: Float?
|
||||||
public let tabsNode: ASDisplayNode?
|
public let tabsNode: ASDisplayNode?
|
||||||
public let tabsNodeIsSearch: Bool
|
public let tabsNodeIsSearch: Bool
|
||||||
@ -39,6 +40,7 @@ public final class ChatListNavigationBar: Component {
|
|||||||
secondaryContent: ChatListHeaderComponent.Content?,
|
secondaryContent: ChatListHeaderComponent.Content?,
|
||||||
secondaryTransition: CGFloat,
|
secondaryTransition: CGFloat,
|
||||||
storySubscriptions: EngineStorySubscriptions?,
|
storySubscriptions: EngineStorySubscriptions?,
|
||||||
|
storiesIncludeHidden: Bool,
|
||||||
uploadProgress: Float?,
|
uploadProgress: Float?,
|
||||||
tabsNode: ASDisplayNode?,
|
tabsNode: ASDisplayNode?,
|
||||||
tabsNodeIsSearch: Bool,
|
tabsNodeIsSearch: Bool,
|
||||||
@ -56,6 +58,7 @@ public final class ChatListNavigationBar: Component {
|
|||||||
self.secondaryContent = secondaryContent
|
self.secondaryContent = secondaryContent
|
||||||
self.secondaryTransition = secondaryTransition
|
self.secondaryTransition = secondaryTransition
|
||||||
self.storySubscriptions = storySubscriptions
|
self.storySubscriptions = storySubscriptions
|
||||||
|
self.storiesIncludeHidden = storiesIncludeHidden
|
||||||
self.uploadProgress = uploadProgress
|
self.uploadProgress = uploadProgress
|
||||||
self.tabsNode = tabsNode
|
self.tabsNode = tabsNode
|
||||||
self.tabsNodeIsSearch = tabsNodeIsSearch
|
self.tabsNodeIsSearch = tabsNodeIsSearch
|
||||||
@ -97,6 +100,9 @@ public final class ChatListNavigationBar: Component {
|
|||||||
if lhs.storySubscriptions != rhs.storySubscriptions {
|
if lhs.storySubscriptions != rhs.storySubscriptions {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.storiesIncludeHidden != rhs.storiesIncludeHidden {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.uploadProgress != rhs.uploadProgress {
|
if lhs.uploadProgress != rhs.uploadProgress {
|
||||||
return false
|
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 {
|
public final class View: UIView {
|
||||||
private let backgroundView: BlurredBackgroundView
|
private let backgroundView: BlurredBackgroundView
|
||||||
private let separatorLayer: SimpleLayer
|
private let separatorLayer: SimpleLayer
|
||||||
@ -142,7 +151,9 @@ public final class ChatListNavigationBar: Component {
|
|||||||
|
|
||||||
private var applyScrollFractionAnimator: DisplayLinkAnimator?
|
private var applyScrollFractionAnimator: DisplayLinkAnimator?
|
||||||
private var applyScrollFraction: CGFloat = 1.0
|
private var applyScrollFraction: CGFloat = 1.0
|
||||||
|
private var applyScrollUnlockedFraction: CGFloat = 1.0
|
||||||
private var applyScrollStartFraction: CGFloat = 0.0
|
private var applyScrollStartFraction: CGFloat = 0.0
|
||||||
|
private var storiesOffsetFraction: CGFloat = 0.0
|
||||||
|
|
||||||
private var tabsNode: ASDisplayNode?
|
private var tabsNode: ASDisplayNode?
|
||||||
private var tabsNodeIsSearch: Bool = false
|
private var tabsNodeIsSearch: Bool = false
|
||||||
@ -201,11 +212,11 @@ public final class ChatListNavigationBar: Component {
|
|||||||
self.scrollTheme = component.theme
|
self.scrollTheme = component.theme
|
||||||
self.scrollStrings = component.strings
|
self.scrollStrings = component.strings
|
||||||
|
|
||||||
let searchOffsetDistance: CGFloat = navigationBarSearchContentHeight
|
let searchOffsetDistance: CGFloat = ChatListNavigationBar.searchScrollHeight
|
||||||
let defaultStoriesOffsetDistance: CGFloat = 79.0
|
let defaultStoriesOffsetDistance: CGFloat = ChatListNavigationBar.storiesScrollHeight
|
||||||
let effectiveStoriesOffsetDistance: CGFloat
|
let effectiveStoriesOffsetDistance: CGFloat
|
||||||
|
|
||||||
var minContentOffset: CGFloat = navigationBarSearchContentHeight
|
var minContentOffset: CGFloat = ChatListNavigationBar.searchScrollHeight
|
||||||
if !component.isSearchActive, let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty, component.storiesUnlocked {
|
if !component.isSearchActive, let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty, component.storiesUnlocked {
|
||||||
effectiveStoriesOffsetDistance = defaultStoriesOffsetDistance * (1.0 - component.secondaryTransition)
|
effectiveStoriesOffsetDistance = defaultStoriesOffsetDistance * (1.0 - component.secondaryTransition)
|
||||||
minContentOffset += effectiveStoriesOffsetDistance
|
minContentOffset += effectiveStoriesOffsetDistance
|
||||||
@ -269,22 +280,32 @@ public final class ChatListNavigationBar: Component {
|
|||||||
self.addSubview(searchContentNode.view)
|
self.addSubview(searchContentNode.view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let clippedStoriesOverscrollOffset = -min(0.0, clippedScrollOffset)
|
||||||
let clippedStoriesOffset = max(0.0, min(clippedScrollOffset, defaultStoriesOffsetDistance))
|
let clippedStoriesOffset = max(0.0, min(clippedScrollOffset, defaultStoriesOffsetDistance))
|
||||||
var storiesOffsetFraction: CGFloat
|
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
|
storiesOffsetFraction = clippedStoriesOffset / defaultStoriesOffsetDistance
|
||||||
|
storiesUnlockedOffsetFraction = 1.0
|
||||||
|
} else {
|
||||||
|
storiesOffsetFraction = 1.0 - (clippedStoriesOverscrollOffset / defaultStoriesOffsetDistance)
|
||||||
|
storiesUnlockedOffsetFraction = 0.0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
storiesOffsetFraction = 1.0
|
storiesOffsetFraction = 1.0
|
||||||
|
storiesUnlockedOffsetFraction = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.applyScrollFractionAnimator != nil {
|
if self.applyScrollFractionAnimator != nil {
|
||||||
storiesOffsetFraction = self.applyScrollFraction * storiesOffsetFraction + (1.0 - self.applyScrollFraction) * 1.0
|
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)
|
let searchSize = CGSize(width: currentLayout.size.width, height: navigationBarSearchContentHeight)
|
||||||
var searchFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height - searchSize.height), size: searchSize)
|
var searchFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height - searchSize.height), size: searchSize)
|
||||||
if component.tabsNode != nil {
|
if component.tabsNode != nil {
|
||||||
searchFrame.origin.y -= 46.0
|
searchFrame.origin.y -= 40.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let clippedSearchOffset = max(0.0, min(clippedScrollOffset - effectiveStoriesOffsetDistance, searchOffsetDistance))
|
let clippedSearchOffset = max(0.0, min(clippedScrollOffset - effectiveStoriesOffsetDistance, searchOffsetDistance))
|
||||||
@ -300,6 +321,7 @@ public final class ChatListNavigationBar: Component {
|
|||||||
headerTransition = .immediate
|
headerTransition = .immediate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.storiesOffsetFraction = storiesOffsetFraction
|
||||||
let headerContentSize = self.headerContent.update(
|
let headerContentSize = self.headerContent.update(
|
||||||
transition: headerTransition,
|
transition: headerTransition,
|
||||||
component: AnyComponent(ChatListHeaderComponent(
|
component: AnyComponent(ChatListHeaderComponent(
|
||||||
@ -309,7 +331,9 @@ public final class ChatListNavigationBar: Component {
|
|||||||
secondaryTransition: component.secondaryTransition,
|
secondaryTransition: component.secondaryTransition,
|
||||||
networkStatus: nil,
|
networkStatus: nil,
|
||||||
storySubscriptions: component.storySubscriptions,
|
storySubscriptions: component.storySubscriptions,
|
||||||
|
storiesIncludeHidden: component.storiesIncludeHidden,
|
||||||
storiesFraction: 1.0 - storiesOffsetFraction,
|
storiesFraction: 1.0 - storiesOffsetFraction,
|
||||||
|
storiesUnlockedFraction: 1.0 - storiesUnlockedOffsetFraction,
|
||||||
uploadProgress: component.uploadProgress,
|
uploadProgress: component.uploadProgress,
|
||||||
context: component.context,
|
context: component.context,
|
||||||
theme: component.theme,
|
theme: component.theme,
|
||||||
@ -375,7 +399,7 @@ public final class ChatListNavigationBar: Component {
|
|||||||
|
|
||||||
if let disappearingTabsView = self.disappearingTabsView {
|
if let disappearingTabsView = self.disappearingTabsView {
|
||||||
disappearingTabsView.layer.anchorPoint = CGPoint()
|
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 {
|
if let tabsNode = component.tabsNode {
|
||||||
@ -401,7 +425,7 @@ public final class ChatListNavigationBar: Component {
|
|||||||
transition.setAlpha(view: tabsNode.view, alpha: 1.0)
|
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
|
self.effectiveStoriesInsetHeight = 0.0
|
||||||
} else {
|
} else {
|
||||||
if let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty, component.storiesUnlocked {
|
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
|
contentHeight += storiesHeight
|
||||||
self.effectiveStoriesInsetHeight = storiesHeight
|
self.effectiveStoriesInsetHeight = storiesHeight
|
||||||
} else {
|
} else {
|
||||||
@ -452,7 +476,7 @@ public final class ChatListNavigationBar: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if component.tabsNode != nil {
|
if component.tabsNode != nil {
|
||||||
contentHeight += 46.0
|
contentHeight += 40.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = CGSize(width: availableSize.width, height: contentHeight)
|
let size = CGSize(width: availableSize.width, height: contentHeight)
|
||||||
@ -467,13 +491,18 @@ public final class ChatListNavigationBar: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if storiesUnlockedUpdated && component.storiesUnlocked {
|
if storiesUnlockedUpdated && component.storiesUnlocked {
|
||||||
self.applyScrollFraction = 0.0
|
|
||||||
self.applyScrollStartFraction = 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
|
self.applyScrollFractionAnimator = DisplayLinkAnimator(duration: 0.3, from: 0.0, to: 1.0, update: { [weak self] value in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
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 {
|
if let rawScrollOffset = self.rawScrollOffset {
|
||||||
self.hasDeferredScrollOffset = true
|
self.hasDeferredScrollOffset = true
|
||||||
|
@ -372,9 +372,10 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
|
|
||||||
func animateOut(completion: @escaping () -> Void) {
|
func animateOut(completion: @escaping () -> Void) {
|
||||||
self.isAnimatingOut = true
|
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) {
|
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))
|
let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut))
|
||||||
transition.setAlpha(layer: self.backgroundLayer, alpha: 0.0)
|
transition.setAlpha(layer: self.backgroundLayer, alpha: 0.0)
|
||||||
transition.setAlpha(view: self.backgroundEffectView, alpha: 0.0)
|
transition.setAlpha(view: self.backgroundEffectView, alpha: 0.0)
|
||||||
@ -385,10 +386,14 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
transitionOutCompleted()
|
transitionOutCompleted()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
self.layer.allowsGroupOpacity = true
|
self.dismissPanState = ItemSetPanState(fraction: 1.0, didBegin: true)
|
||||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
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()
|
completion()
|
||||||
})
|
})
|
||||||
|
transition.setAlpha(view: self.backgroundEffectView, alpha: 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.didAnimateOut = true
|
self.didAnimateOut = true
|
||||||
|
@ -1061,8 +1061,31 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
if component.slice.item.storyItem.isPublic {
|
if component.slice.item.storyItem.isPublic {
|
||||||
items.append(.action(ContextMenuActionItem(text: "Copy link", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: "Copy link", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { _, a in
|
}, action: { [weak self] _, a in
|
||||||
a(.default)
|
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
|
items.append(.action(ContextMenuActionItem(text: "Share", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
|
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 let inlineActionsView = self.inlineActions.view {
|
||||||
if inlineActionsView.superview == nil {
|
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
|
var inlineActionsAlpha: CGFloat = inputPanelIsOverlay ? 0.0 : 1.0
|
||||||
if self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil {
|
if self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil {
|
||||||
@ -1666,6 +1689,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
if component.hideUI || self.displayViewList {
|
if component.hideUI || self.displayViewList {
|
||||||
inlineActionsAlpha = 0.0
|
inlineActionsAlpha = 0.0
|
||||||
}
|
}
|
||||||
|
if !component.slice.item.storyItem.isPublic {
|
||||||
|
inlineActionsAlpha = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
transition.setAlpha(view: inlineActionsView, alpha: inlineActionsAlpha)
|
transition.setAlpha(view: inlineActionsView, alpha: inlineActionsAlpha)
|
||||||
}
|
}
|
||||||
|
@ -273,40 +273,26 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func performInlineAction(view: StoryItemSetContainerComponent.View, item: StoryActionsComponent.Item) {
|
func performInlineAction(view: StoryItemSetContainerComponent.View, item: StoryActionsComponent.Item) {
|
||||||
/*guard let component = view.component else {
|
guard let component = view.component else {
|
||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch item.kind {
|
switch item.kind {
|
||||||
case .like:
|
case .like:
|
||||||
if item.isActivated {
|
return
|
||||||
component.context.engine.messages.setMessageReactions(
|
|
||||||
id: targetMessageId,
|
|
||||||
reactions: [
|
|
||||||
]
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
component.context.engine.messages.setMessageReactions(
|
|
||||||
id: targetMessageId,
|
|
||||||
reactions: [
|
|
||||||
.builtin("❤")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case .share:
|
case .share:
|
||||||
let _ = (component.context.engine.data.get(
|
let _ = (component.context.engine.messages.exportStoryLink(peerId: peerId, id: focusedItem.storyItem.id)
|
||||||
TelegramEngine.EngineData.Item.Messages.Message(id: targetMessageId)
|
|> deliverOnMainQueue).start(next: { [weak view] link in
|
||||||
)
|
guard let view, let link, let component = view.component, let controller = component.controller() else {
|
||||||
|> deliverOnMainQueue).start(next: { [weak view] message in
|
|
||||||
guard let view, let message, let component = view.component, let controller = component.controller() else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let shareController = ShareController(
|
let shareController = ShareController(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
subject: .messages([message._asMessage()]),
|
subject: .url(link),
|
||||||
externalShare: false,
|
externalShare: false,
|
||||||
immediateExternalShare: false,
|
immediateExternalShare: false,
|
||||||
updatedPresentationData: (component.context.sharedContext.currentPresentationData.with({ $0 }),
|
updatedPresentationData: (component.context.sharedContext.currentPresentationData.with({ $0 }),
|
||||||
@ -314,7 +300,7 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
)
|
)
|
||||||
controller.present(shareController, in: .window(.root))
|
controller.present(shareController, in: .window(.root))
|
||||||
})
|
})
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func clearInputText(view: StoryItemSetContainerComponent.View) {
|
private func clearInputText(view: StoryItemSetContainerComponent.View) {
|
||||||
|
@ -310,6 +310,7 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
private let includeHidden: Bool
|
||||||
|
|
||||||
public private(set) var stateValue: StoryContentContextState?
|
public private(set) var stateValue: StoryContentContextState?
|
||||||
public var state: Signal<StoryContentContextState, NoError> {
|
public var state: Signal<StoryContentContextState, NoError> {
|
||||||
@ -342,14 +343,16 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
|
includeHidden: Bool,
|
||||||
focusedPeerId: EnginePeer.Id?
|
focusedPeerId: EnginePeer.Id?
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.includeHidden = includeHidden
|
||||||
if let focusedPeerId {
|
if let focusedPeerId {
|
||||||
self.focusedItem = (focusedPeerId, nil)
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] storySubscriptions in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -741,6 +744,16 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
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 {
|
if let item, case let .item(itemValue) = item, let media = itemValue.media, let peer {
|
||||||
let mappedItem = EngineStoryItem(
|
let mappedItem = EngineStoryItem(
|
||||||
id: itemValue.id,
|
id: itemValue.id,
|
||||||
|
@ -21,8 +21,10 @@ public final class StoryPeerListComponent: Component {
|
|||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
public let strings: PresentationStrings
|
public let strings: PresentationStrings
|
||||||
|
public let includesHidden: Bool
|
||||||
public let storySubscriptions: EngineStorySubscriptions?
|
public let storySubscriptions: EngineStorySubscriptions?
|
||||||
public let collapseFraction: CGFloat
|
public let collapseFraction: CGFloat
|
||||||
|
public let unlockedFraction: CGFloat
|
||||||
public let uploadProgress: Float?
|
public let uploadProgress: Float?
|
||||||
public let peerAction: (EnginePeer?) -> Void
|
public let peerAction: (EnginePeer?) -> Void
|
||||||
public let contextPeerAction: (ContextExtractedContentContainingNode, ContextGesture, EnginePeer) -> Void
|
public let contextPeerAction: (ContextExtractedContentContainingNode, ContextGesture, EnginePeer) -> Void
|
||||||
@ -32,8 +34,10 @@ public final class StoryPeerListComponent: Component {
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
strings: PresentationStrings,
|
strings: PresentationStrings,
|
||||||
|
includesHidden: Bool,
|
||||||
storySubscriptions: EngineStorySubscriptions?,
|
storySubscriptions: EngineStorySubscriptions?,
|
||||||
collapseFraction: CGFloat,
|
collapseFraction: CGFloat,
|
||||||
|
unlockedFraction: CGFloat,
|
||||||
uploadProgress: Float?,
|
uploadProgress: Float?,
|
||||||
peerAction: @escaping (EnginePeer?) -> Void,
|
peerAction: @escaping (EnginePeer?) -> Void,
|
||||||
contextPeerAction: @escaping (ContextExtractedContentContainingNode, ContextGesture, EnginePeer) -> Void
|
contextPeerAction: @escaping (ContextExtractedContentContainingNode, ContextGesture, EnginePeer) -> Void
|
||||||
@ -42,8 +46,10 @@ public final class StoryPeerListComponent: Component {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
self.includesHidden = includesHidden
|
||||||
self.storySubscriptions = storySubscriptions
|
self.storySubscriptions = storySubscriptions
|
||||||
self.collapseFraction = collapseFraction
|
self.collapseFraction = collapseFraction
|
||||||
|
self.unlockedFraction = unlockedFraction
|
||||||
self.uploadProgress = uploadProgress
|
self.uploadProgress = uploadProgress
|
||||||
self.peerAction = peerAction
|
self.peerAction = peerAction
|
||||||
self.contextPeerAction = contextPeerAction
|
self.contextPeerAction = contextPeerAction
|
||||||
@ -59,12 +65,18 @@ public final class StoryPeerListComponent: Component {
|
|||||||
if lhs.strings !== rhs.strings {
|
if lhs.strings !== rhs.strings {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.includesHidden != rhs.includesHidden {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.storySubscriptions != rhs.storySubscriptions {
|
if lhs.storySubscriptions != rhs.storySubscriptions {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.collapseFraction != rhs.collapseFraction {
|
if lhs.collapseFraction != rhs.collapseFraction {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.unlockedFraction != rhs.unlockedFraction {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.uploadProgress != rhs.uploadProgress {
|
if lhs.uploadProgress != rhs.uploadProgress {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -211,7 +223,7 @@ public final class StoryPeerListComponent: Component {
|
|||||||
}
|
}
|
||||||
let _ = hasStories
|
let _ = hasStories
|
||||||
|
|
||||||
let collapseStartIndex = 1
|
let collapseStartIndex = component.includesHidden ? 0 : 1
|
||||||
|
|
||||||
let collapsedItemWidth: CGFloat = 24.0
|
let collapsedItemWidth: CGFloat = 24.0
|
||||||
let collapsedItemDistance: CGFloat = 14.0
|
let collapsedItemDistance: CGFloat = 14.0
|
||||||
@ -290,7 +302,14 @@ public final class StoryPeerListComponent: Component {
|
|||||||
|
|
||||||
let itemFrame: CGRect
|
let itemFrame: CGRect
|
||||||
if isReallyVisible {
|
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 {
|
} else {
|
||||||
itemFrame = regularItemFrame
|
itemFrame = regularItemFrame
|
||||||
}
|
}
|
||||||
@ -315,7 +334,7 @@ public final class StoryPeerListComponent: Component {
|
|||||||
rightItemFrame = regularRightItemFrame.interpolate(to: collapsedRightItemFrame, amount: component.collapseFraction)
|
rightItemFrame = regularRightItemFrame.interpolate(to: collapsedRightItemFrame, amount: component.collapseFraction)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if component.collapseFraction == 1.0 {
|
if component.collapseFraction == 1.0 || component.unlockedFraction == 0.0 {
|
||||||
itemAlpha = 0.0
|
itemAlpha = 0.0
|
||||||
} else {
|
} else {
|
||||||
itemAlpha = 1.0
|
itemAlpha = 1.0
|
||||||
@ -440,9 +459,16 @@ public final class StoryPeerListComponent: Component {
|
|||||||
if let storySubscriptions = component.storySubscriptions, let hasMoreToken = storySubscriptions.hasMoreToken {
|
if let storySubscriptions = component.storySubscriptions, let hasMoreToken = storySubscriptions.hasMoreToken {
|
||||||
if self.requestedLoadMoreToken != hasMoreToken {
|
if self.requestedLoadMoreToken != hasMoreToken {
|
||||||
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()
|
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)
|
self.sortedItems.removeAll(keepingCapacity: true)
|
||||||
if let storySubscriptions = component.storySubscriptions {
|
if let storySubscriptions = component.storySubscriptions {
|
||||||
if let accountItem = storySubscriptions.accountItem {
|
if !component.includesHidden, let accountItem = storySubscriptions.accountItem {
|
||||||
self.sortedItems.append(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 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
|
let avatarNode: AvatarNode
|
||||||
if let current = self.avatarNode {
|
if let current = self.avatarNode {
|
||||||
@ -577,7 +577,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: availableSize.width + 4.0, height: 100.0)
|
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 let titleView = self.title.view {
|
||||||
if titleView.superview == nil {
|
if titleView.superview == nil {
|
||||||
titleView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
|
titleView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
|
||||||
|
@ -1183,7 +1183,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
ignoreForward = true
|
ignoreForward = true
|
||||||
effectiveAuthor = forwardInfo.author
|
effectiveAuthor = forwardInfo.author
|
||||||
if effectiveAuthor == nil, let authorSignature = forwardInfo.authorSignature {
|
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
|
displayAuthorInfo = !mergedTop.merged && incoming && effectiveAuthor != nil
|
||||||
@ -1199,7 +1199,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
displayAuthorInfo = !mergedTop.merged && incoming
|
displayAuthorInfo = !mergedTop.merged && incoming
|
||||||
} else if let forwardInfo = item.content.firstMessage.forwardInfo, forwardInfo.flags.contains(.isImported), let authorSignature = forwardInfo.authorSignature {
|
} else if let forwardInfo = item.content.firstMessage.forwardInfo, forwardInfo.flags.contains(.isImported), let authorSignature = forwardInfo.authorSignature {
|
||||||
ignoreForward = true
|
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
|
displayAuthorInfo = !mergedTop.merged && incoming
|
||||||
} else if let adAttribute = item.content.firstMessage.adAttribute, let author = item.content.firstMessage.author {
|
} else if let adAttribute = item.content.firstMessage.adAttribute, let author = item.content.firstMessage.author {
|
||||||
ignoreForward = true
|
ignoreForward = true
|
||||||
|
@ -338,7 +338,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
|
|||||||
if let forwardInfo = content.firstMessage.forwardInfo {
|
if let forwardInfo = content.firstMessage.forwardInfo {
|
||||||
effectiveAuthor = forwardInfo.author
|
effectiveAuthor = forwardInfo.author
|
||||||
if effectiveAuthor == nil, let authorSignature = forwardInfo.authorSignature {
|
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
|
displayAuthorInfo = incoming && effectiveAuthor != nil
|
||||||
|
@ -978,6 +978,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
break
|
break
|
||||||
case .gameStart:
|
case .gameStart:
|
||||||
break
|
break
|
||||||
|
case .story:
|
||||||
|
break
|
||||||
case let .channelMessage(peer, messageId, timecode):
|
case let .channelMessage(peer, messageId, timecode):
|
||||||
if let navigationController = strongSelf.getNavigationController() {
|
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)))
|
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)
|
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)
|
self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
}
|
}
|
||||||
|
@ -231,7 +231,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
var headerInsets = layout.insets(options: [.input])
|
var headerInsets = layout.insets(options: [.input])
|
||||||
headerInsets.top += actualNavigationBarHeight
|
headerInsets.top += actualNavigationBarHeight
|
||||||
headerInsets.top += strongSelf.tokenListNode.bounds.size.height
|
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)
|
searchResultsNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +285,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
switch self.contentNode {
|
switch self.contentNode {
|
||||||
case let .contacts(contactsNode):
|
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):
|
case let .chats(chatsNode):
|
||||||
var combinedInsets = insets
|
var combinedInsets = insets
|
||||||
combinedInsets.left += layout.safeInsets.left
|
combinedInsets.left += layout.safeInsets.left
|
||||||
@ -297,7 +297,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
self.contentNode.node.frame = CGRect(origin: CGPoint(), size: layout.size)
|
self.contentNode.node.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
|
|
||||||
if let searchResultsNode = self.searchResultsNode {
|
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)
|
searchResultsNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
|||||||
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
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)
|
self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@ import BotPaymentsUI
|
|||||||
import PremiumUI
|
import PremiumUI
|
||||||
import AuthorizationUI
|
import AuthorizationUI
|
||||||
import ChatFolderLinkPreviewScreen
|
import ChatFolderLinkPreviewScreen
|
||||||
|
import StoryContainerScreen
|
||||||
|
import StoryContentComponent
|
||||||
|
|
||||||
private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer {
|
private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer {
|
||||||
if case .default = navigation {
|
if case .default = navigation {
|
||||||
@ -803,5 +805,24 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
|||||||
}))
|
}))
|
||||||
dismissInput()
|
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.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.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 {
|
if let searchDisplayController = self.searchDisplayController {
|
||||||
|
@ -250,7 +250,7 @@ private func pollResultsControllerEntries(presentationData: PresentationData, po
|
|||||||
displayCount = Int(voterCount)
|
displayCount = Int(voterCount)
|
||||||
}
|
}
|
||||||
for peerIndex in 0 ..< displayCount {
|
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))
|
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))
|
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 replyThread(Int32, Int32)
|
||||||
case voiceChat(String?)
|
case voiceChat(String?)
|
||||||
case appStart(String, String?)
|
case appStart(String, String?)
|
||||||
|
case story(Int32)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ParsedInternalUrl {
|
public enum ParsedInternalUrl {
|
||||||
@ -246,6 +247,10 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .startAttach(peerName, value, choose)
|
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) {
|
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||||
return .peer(.name(peerName), .voiceChat(nil))
|
return .peer(.name(peerName), .voiceChat(nil))
|
||||||
@ -697,6 +702,8 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
|||||||
}
|
}
|
||||||
case let .voiceChat(invite):
|
case let .voiceChat(invite):
|
||||||
return .single(.joinVoiceChat(peer.id, invite))
|
return .single(.joinVoiceChat(peer.id, invite))
|
||||||
|
case let .story(id):
|
||||||
|
return .single(.story(peerId: peer.id, id: id))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user