mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-06-08 11:03:55 +02:00
chore: migrate to new version + fixed several critical bugs
- Migrated project to latest Telegram iOS base (v12.3.2+) - Fixed circular dependency between GhostModeManager and MiscSettingsManager - Fixed multiple Bazel build configuration errors (select() default conditions) - Fixed duplicate type definitions in PeerInfoScreen - Fixed swiftmodule directory resolution in build scripts - Added Ghostgram Settings tab in main Settings menu with all 5 features - Cleared sensitive credentials from config.json (template-only now) - Excluded bazel-cache from version control
This commit is contained in:
@@ -29,7 +29,6 @@ swift_library(
|
||||
"//submodules/ActivityIndicator:ActivityIndicator",
|
||||
"//submodules/SearchBarNode:SearchBarNode",
|
||||
"//submodules/ChatListSearchRecentPeersNode:ChatListSearchRecentPeersNode",
|
||||
"//submodules/ChatListSearchItemNode:ChatListSearchItemNode",
|
||||
"//submodules/ChatListSearchItemHeader:ChatListSearchItemHeader",
|
||||
"//submodules/TemporaryCachedPeerDataManager:TemporaryCachedPeerDataManager",
|
||||
"//submodules/PeerPresenceStatusManager:PeerPresenceStatusManager",
|
||||
@@ -118,6 +117,14 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode",
|
||||
"//submodules/TelegramUI/Components/HeaderPanelContainerComponent",
|
||||
"//submodules/TelegramUI/Components/HorizontalTabsComponent",
|
||||
"//submodules/TelegramUI/Components/GlobalControlPanelsContext",
|
||||
"//submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent",
|
||||
"//submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent",
|
||||
"//submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode",
|
||||
"//submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -169,7 +169,7 @@ public class ChatListAdditionalCategoryItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
self.titleNode = TextNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.isAccessibilityElement = true
|
||||
|
||||
|
||||
@@ -450,10 +450,10 @@ final class ChatListContainerItemNode: ASDisplayNode {
|
||||
|
||||
self.layoutAdditionalPanels(transition: transition)
|
||||
|
||||
let edgeEffectHeight: CGFloat = insets.bottom
|
||||
let edgeEffectHeight: CGFloat = insets.bottom + 8.0
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - edgeEffectHeight), size: CGSize(width: size.width, height: edgeEffectHeight))
|
||||
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, transition: ComponentTransition(transition))
|
||||
self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectFrame.height, 40.0), transition: ComponentTransition(transition))
|
||||
transition.updateAlpha(layer: self.edgeEffectView.layer, alpha: edgeEffectHeight > 21.0 ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,13 @@ import ChatFolderLinkPreviewScreen
|
||||
import ChatListHeaderComponent
|
||||
import StoryPeerListComponent
|
||||
import TelegramNotices
|
||||
import EdgeEffect
|
||||
import HeaderPanelContainerComponent
|
||||
import HorizontalTabsComponent
|
||||
import PremiumUI
|
||||
import MediaPlaybackHeaderPanelComponent
|
||||
import LiveLocationHeaderPanelComponent
|
||||
import ChatListHeaderNoticeComponent
|
||||
import ChatListFilterTabContainerNode
|
||||
|
||||
public enum ChatListContainerNodeFilter: Equatable {
|
||||
case all
|
||||
@@ -132,6 +138,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
}
|
||||
|
||||
public var currentItemFilterUpdated: ((ChatListFilterTabEntryId, CGFloat, ContainedViewLayoutTransition, Bool) -> Void)?
|
||||
public private(set) var isSwitchingCurrentItemFilterByDragging: Bool = false
|
||||
public var currentItemFilter: ChatListFilterTabEntryId {
|
||||
return self.currentItemNode.chatListFilter.flatMap { .filter($0.id) } ?? .all
|
||||
}
|
||||
@@ -575,6 +582,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
itemNode.layer.removeAllAnimations()
|
||||
}
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .immediate)
|
||||
self.isSwitchingCurrentItemFilterByDragging = true
|
||||
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, .immediate, true)
|
||||
}
|
||||
}
|
||||
@@ -651,6 +659,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
}
|
||||
}
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .immediate)
|
||||
self.isSwitchingCurrentItemFilterByDragging = true
|
||||
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false)
|
||||
}
|
||||
case .cancelled, .ended:
|
||||
@@ -712,6 +721,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
if let switchToId = applyNodeAsCurrent, let itemNode = self.itemNodes[switchToId] {
|
||||
self.applyItemNodeAsCurrent(id: switchToId, itemNode: itemNode)
|
||||
}
|
||||
self.isSwitchingCurrentItemFilterByDragging = false
|
||||
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false)
|
||||
}
|
||||
default:
|
||||
@@ -1091,9 +1101,10 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
private var toolbarNode: ToolbarNode?
|
||||
var toolbarActionSelected: ((ToolbarActionOption) -> Void)?
|
||||
|
||||
private var isSearchDisplayControllerActive: Bool = false
|
||||
private var isSearchDisplayControllerActive: ChatListNavigationBar.ActiveSearch?
|
||||
private var skipSearchDisplayControllerLayout: Bool = false
|
||||
private(set) var searchDisplayController: SearchDisplayController?
|
||||
private var disappearingSearchDisplayController: SearchDisplayController?
|
||||
|
||||
var isReorderingFilters: Bool = false
|
||||
var didBeginSelectingChatsWhileEditing: Bool = false
|
||||
@@ -1353,14 +1364,226 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
private func updateNavigationBar(layout: ContainerViewLayout, deferScrollApplication: Bool, transition: ComponentTransition) -> (navigationHeight: CGFloat, storiesInset: CGFloat) {
|
||||
let headerContent = self.controller?.updateHeaderContent()
|
||||
|
||||
var tabsNode: ASDisplayNode?
|
||||
var tabsNodeIsSearch = false
|
||||
var panels: [HeaderPanelContainerComponent.Panel] = []
|
||||
if let chatListNotice = self.controller?.globalControlPanelsContextState?.chatListNotice {
|
||||
panels.append(HeaderPanelContainerComponent.Panel(
|
||||
key: "chatListNotice",
|
||||
orderIndex: 0,
|
||||
component: AnyComponent(ChatListHeaderNoticeComponent(
|
||||
context: self.context,
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
data: chatListNotice,
|
||||
activateAction: { [weak self] notice in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
switch notice {
|
||||
case .clearStorage:
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openStorageManagement()
|
||||
case .setupPassword:
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openPasswordSetup()
|
||||
case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore:
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openPremiumIntro()
|
||||
case .xmasPremiumGift:
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openPremiumGift([], nil)
|
||||
case .premiumGrace:
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openPremiumManagement()
|
||||
case .setupBirthday:
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openBirthdaySetup()
|
||||
case let .birthdayPremiumGift(peers, birthdays):
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openPremiumGift(peers, birthdays)
|
||||
case .reviewLogin:
|
||||
break
|
||||
case let .starsSubscriptionLowBalance(amount, _):
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openStarsTopup(amount.value)
|
||||
case .setupPhoto:
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openPhotoSetup()
|
||||
case .accountFreeze:
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openAccountFreezeInfo()
|
||||
case let .link(_, url, _, _):
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.openUrl(url)
|
||||
}
|
||||
},
|
||||
dismissAction: { [weak self] notice in
|
||||
guard let self, let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
controller.globalControlPanelsContext.dismissChatListNotice(parentController: controller, notice: notice)
|
||||
},
|
||||
selectAction: { [weak self] notice, isPositive in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
switch notice {
|
||||
case let .reviewLogin(newSessionReview, _):
|
||||
self.effectiveContainerNode.currentItemNode.interaction?.performActiveSessionAction(newSessionReview, isPositive)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
)))
|
||||
)
|
||||
}
|
||||
if let mediaPlayback = self.controller?.globalControlPanelsContextState?.mediaPlayback {
|
||||
panels.append(HeaderPanelContainerComponent.Panel(
|
||||
key: "media",
|
||||
orderIndex: 1,
|
||||
component: AnyComponent(MediaPlaybackHeaderPanelComponent(
|
||||
context: self.context,
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
data: mediaPlayback,
|
||||
controller: { [weak self] in
|
||||
return self?.controller
|
||||
}
|
||||
)))
|
||||
)
|
||||
}
|
||||
if let liveLocation = self.controller?.globalControlPanelsContextState?.liveLocation {
|
||||
panels.append(HeaderPanelContainerComponent.Panel(
|
||||
key: "liveLocation",
|
||||
orderIndex: 2,
|
||||
component: AnyComponent(LiveLocationHeaderPanelComponent(
|
||||
context: self.context,
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
data: liveLocation,
|
||||
controller: { [weak self] in
|
||||
return self?.controller
|
||||
}
|
||||
)))
|
||||
)
|
||||
}
|
||||
|
||||
if let value = self.controller?.searchTabsNode {
|
||||
tabsNode = value
|
||||
tabsNodeIsSearch = true
|
||||
} else if let value = self.controller?.tabsNode, self.controller?.hasTabs == true {
|
||||
tabsNode = value
|
||||
var navigationHeaderPanels: AnyComponent<Empty>?
|
||||
if self.controller?.tabContainerData != nil || !panels.isEmpty {
|
||||
var tabs: AnyComponent<Empty>?
|
||||
if let tabContainerData = self.controller?.tabContainerData, tabContainerData.0.count > 1 {
|
||||
let selectedTab: HorizontalTabsComponent.Tab.Id
|
||||
switch self.effectiveContainerNode.currentItemFilter {
|
||||
case .all:
|
||||
selectedTab = AnyHashable(Int32.min)
|
||||
case let .filter(id):
|
||||
selectedTab = AnyHashable(id)
|
||||
}
|
||||
|
||||
let isEditing = self.isReorderingFilters || (self.mainContainerNode.currentItemNode.currentState.editing && !self.didBeginSelectingChatsWhileEditing)
|
||||
|
||||
tabs = AnyComponent(HorizontalTabsComponent(
|
||||
context: self.context,
|
||||
theme: self.presentationData.theme,
|
||||
tabs: tabContainerData.0.map { entry -> HorizontalTabsComponent.Tab in
|
||||
let id: HorizontalTabsComponent.Tab.Id
|
||||
let title: HorizontalTabsComponent.Tab.Title
|
||||
var badge: HorizontalTabsComponent.Tab.Badge?
|
||||
var isMainTab = false
|
||||
switch entry {
|
||||
case .all:
|
||||
id = Int32.min
|
||||
title = HorizontalTabsComponent.Tab.Title(text: self.presentationData.strings.ChatList_Tabs_All, entities: [], enableAnimations: false)
|
||||
isMainTab = true
|
||||
case let .filter(idValue, text, unread):
|
||||
id = AnyHashable(idValue)
|
||||
title = HorizontalTabsComponent.Tab.Title(text: text.text, entities: text.entities, enableAnimations: text.enableAnimations)
|
||||
if unread.value != 0 {
|
||||
badge = HorizontalTabsComponent.Tab.Badge(
|
||||
title: "\(unread.value)",
|
||||
isAccent: unread.hasUnmuted
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return HorizontalTabsComponent.Tab(
|
||||
id: id,
|
||||
content: .title(title),
|
||||
badge: badge,
|
||||
action: { [weak self] in
|
||||
guard let self, let tabContainerData = self.controller?.tabContainerData else {
|
||||
return
|
||||
}
|
||||
|
||||
let isPremium = self.context.isPremium
|
||||
|
||||
let mappedId: ChatListFilterTabEntryId = entry.id
|
||||
|
||||
var isDisabled = false
|
||||
if let filtersLimit = tabContainerData.2 {
|
||||
guard let folderIndex = tabContainerData.0.firstIndex(where: { $0.id == mappedId }) else {
|
||||
return
|
||||
}
|
||||
isDisabled = !isPremium && folderIndex >= filtersLimit
|
||||
}
|
||||
|
||||
if isDisabled {
|
||||
let filtersCount = tabContainerData.0.count(where: { item in
|
||||
if case .all = item {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
let context = self.context
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .folders, count: Int32(filtersCount), action: {
|
||||
let controller = PremiumIntroScreen(context: context, source: .folders)
|
||||
replaceImpl?(controller)
|
||||
return true
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
self.controller?.push(controller)
|
||||
} else {
|
||||
self.controller?.selectTab(id: mappedId)
|
||||
}
|
||||
},
|
||||
contextAction: { [weak self] sourceView, gesture in
|
||||
guard let self, let tabContainerData = self.controller?.tabContainerData else {
|
||||
return
|
||||
}
|
||||
|
||||
let isPremium = self.context.isPremium
|
||||
|
||||
let mappedId: Int32?
|
||||
switch entry {
|
||||
case .all:
|
||||
mappedId = nil
|
||||
case let .filter(idValue, _, _):
|
||||
mappedId = idValue
|
||||
}
|
||||
|
||||
var isDisabled = false
|
||||
if let filtersLimit = tabContainerData.2 {
|
||||
guard let folderIndex = tabContainerData.0.firstIndex(where: { $0.id == entry.id }) else {
|
||||
return
|
||||
}
|
||||
isDisabled = !isPremium && folderIndex >= filtersLimit
|
||||
}
|
||||
|
||||
self.controller?.tabContextGesture(id: mappedId, sourceNode: nil, sourceView: sourceView, gesture: gesture, keepInPlace: false, isDisabled: isDisabled)
|
||||
},
|
||||
deleteAction: (!isEditing || isMainTab) ? nil : { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if case let .filter(id) = entry.id {
|
||||
self.controller?.askForFilterRemoval(id: id)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
selectedTab: selectedTab,
|
||||
isEditing: isEditing,
|
||||
liftWhileSwitching: layout.deviceMetrics.type != .tablet
|
||||
))
|
||||
}
|
||||
|
||||
navigationHeaderPanels = AnyComponent(HeaderPanelContainerComponent(
|
||||
theme: self.presentationData.theme,
|
||||
tabs: tabs,
|
||||
panels: panels
|
||||
))
|
||||
}
|
||||
|
||||
var effectiveStorySubscriptions: EngineStorySubscriptions?
|
||||
@@ -1382,16 +1605,17 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
strings: self.presentationData.strings,
|
||||
statusBarHeight: layout.statusBarHeight ?? 0.0,
|
||||
sideInset: layout.safeInsets.left,
|
||||
isSearchActive: self.isSearchDisplayControllerActive,
|
||||
isSearchEnabled: true,
|
||||
search: ChatListNavigationBar.Search(isEnabled: true),
|
||||
activeSearch: self.isSearchDisplayControllerActive,
|
||||
primaryContent: headerContent?.primaryContent,
|
||||
secondaryContent: headerContent?.secondaryContent,
|
||||
secondaryTransition: self.inlineStackContainerTransitionFraction,
|
||||
storySubscriptions: effectiveStorySubscriptions,
|
||||
storiesIncludeHidden: self.location == .chatList(groupId: .archive),
|
||||
uploadProgress: self.controller?.storyUploadProgress ?? [:],
|
||||
tabsNode: tabsNode,
|
||||
tabsNodeIsSearch: tabsNodeIsSearch,
|
||||
headerPanels: navigationHeaderPanels,
|
||||
tabsNode: nil,
|
||||
tabsNodeIsSearch: false,
|
||||
accessoryPanelContainer: self.controller?.accessoryPanelContainer,
|
||||
accessoryPanelContainerHeight: self.controller?.accessoryPanelContainerHeight ?? 0.0,
|
||||
activateSearch: { [weak self] searchContentNode in
|
||||
@@ -1479,7 +1703,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
var offset = resultingOffset
|
||||
if self.isSearchDisplayControllerActive {
|
||||
if self.isSearchDisplayControllerActive != nil {
|
||||
offset = 0.0
|
||||
}
|
||||
|
||||
@@ -1656,6 +1880,9 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: transition)
|
||||
}
|
||||
}
|
||||
if let disappearingSearchDisplayController = self.disappearingSearchDisplayController {
|
||||
disappearingSearchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: transition)
|
||||
}
|
||||
|
||||
self.updateNavigationScrolling(navigationHeight: navigationBarLayout.navigationHeight, transition: transition)
|
||||
|
||||
@@ -1666,7 +1893,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) async -> (ASDisplayNode, (Bool) -> Void)? {
|
||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode?, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?, searchBarIsExternal: Bool) async -> ((Bool) -> Void)? {
|
||||
guard let (containerLayout, _, _, cleanNavigationBarHeight, _) = self.containerLayout, self.searchDisplayController == nil else {
|
||||
return nil
|
||||
}
|
||||
@@ -1712,16 +1939,16 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
if let requestDeactivateSearch = self?.requestDeactivateSearch {
|
||||
requestDeactivateSearch()
|
||||
}
|
||||
})
|
||||
}, fieldStyle: placeholderNode?.fieldStyle ?? .modern, searchBarIsExternal: searchBarIsExternal)
|
||||
self.mainContainerNode.accessibilityElementsHidden = true
|
||||
self.inlineStackContainerNode?.accessibilityElementsHidden = true
|
||||
|
||||
return (contentNode.filterContainerNode, { [weak self] focus in
|
||||
return ({ [weak self] focus in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.isSearchDisplayControllerActive = true
|
||||
strongSelf.isSearchDisplayControllerActive = ChatListNavigationBar.ActiveSearch(isExternal: placeholderNode == nil)
|
||||
|
||||
strongSelf.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: cleanNavigationBarHeight, transition: .immediate)
|
||||
strongSelf.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in
|
||||
@@ -1731,7 +1958,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
|
||||
if isSearchBar {
|
||||
if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
|
||||
navigationBarComponentView.addSubnode(subnode)
|
||||
navigationBarComponentView.searchContentNode?.addSubnode(subnode)
|
||||
}
|
||||
} else {
|
||||
self.insertSubnode(subnode, aboveSubnode: self.debugListView)
|
||||
@@ -1742,21 +1969,31 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
})
|
||||
}
|
||||
|
||||
func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) -> (() -> Void)? {
|
||||
func deactivateSearch(placeholderNode: SearchBarPlaceholderNode?, animated: Bool) -> (() -> Void)? {
|
||||
if let searchDisplayController = self.searchDisplayController {
|
||||
self.isSearchDisplayControllerActive = false
|
||||
self.isSearchDisplayControllerActive = nil
|
||||
self.searchDisplayController = nil
|
||||
self.disappearingSearchDisplayController = searchDisplayController
|
||||
self.mainContainerNode.accessibilityElementsHidden = false
|
||||
self.inlineStackContainerNode?.accessibilityElementsHidden = false
|
||||
|
||||
return { [weak self, weak placeholderNode] in
|
||||
if let strongSelf = self, let placeholderNode, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout {
|
||||
searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated)
|
||||
|
||||
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
|
||||
|
||||
strongSelf.controller?.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
guard let self, let (layout, _, _, cleanNavigationBarHeight, _) = self.containerLayout else {
|
||||
return
|
||||
}
|
||||
let placeholderNode = placeholderNode
|
||||
searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated, completion: { [weak self, weak searchDisplayController] in
|
||||
guard let self, let searchDisplayController else {
|
||||
return
|
||||
}
|
||||
if self.disappearingSearchDisplayController === searchDisplayController {
|
||||
self.disappearingSearchDisplayController = nil
|
||||
}
|
||||
})
|
||||
|
||||
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
|
||||
|
||||
self.controller?.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
|
||||
@@ -148,7 +148,7 @@ class ChatListFilterPresetCategoryItemNode: ItemListRevealOptionsItemNode, ItemL
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.isAccessibilityElement = true
|
||||
|
||||
|
||||
@@ -1283,7 +1283,6 @@ private final class ChatListFilterPresetController: ItemListController {
|
||||
pendingUnpinnedAllMessages: false,
|
||||
activeGroupCallInfo: nil,
|
||||
hasActiveGroupCall: false,
|
||||
importState: nil,
|
||||
threadData: nil,
|
||||
isGeneralThreadClosed: nil,
|
||||
replyMessage: nil,
|
||||
@@ -1753,14 +1752,14 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi
|
||||
if initialPreset == nil {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text = presentationData.strings.ChatListFilter_AlertCreateFolderBeforeSharingText
|
||||
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
} else {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let state = stateValue.with({ $0 })
|
||||
if state.additionallyIncludePeers.isEmpty {
|
||||
let text = presentationData.strings.ChatListFilter_ErrorShareInvalidFolder
|
||||
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1785,7 +1784,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi
|
||||
statusController?.dismiss()
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: unavailableText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: unavailableText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -2360,7 +2359,7 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec
|
||||
case .someUserTooManyChannels:
|
||||
text = presentationData.strings.ChatListFilter_CreateLinkErrorSomeoneHasChannelLimit
|
||||
}
|
||||
presentController(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
|
||||
presentController(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import PresentationDataUtils
|
||||
import ItemListUI
|
||||
import AccountContext
|
||||
import ItemListPeerActionItem
|
||||
@@ -516,13 +517,13 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch
|
||||
}
|
||||
|
||||
if hasLinks {
|
||||
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatList_AlertDeleteFolderTitle, text: presentationData.strings.ChatList_AlertDeleteFolderText, actions: [
|
||||
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.ChatList_AlertDeleteFolderTitle, text: presentationData.strings.ChatList_AlertDeleteFolderText, actions: [
|
||||
TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: {
|
||||
confirmDeleteFolder()
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
})
|
||||
]))
|
||||
], actionLayout: .vertical))
|
||||
} else {
|
||||
confirmDeleteFolder()
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
self.containerNode.addSubnode(self.titleNode.textNode)
|
||||
|
||||
@@ -132,7 +132,7 @@ public class ChatListFilterPresetListSuggestedItemNode: ListViewItemNode, ItemLi
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
|
||||
@@ -113,7 +113,7 @@ public class ChatListFilterTagSectionHeaderItemNode: ListViewItemNode {
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
self.activateArea.accessibilityTraits = [.staticText, .header]
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.accessoryTextNode)
|
||||
|
||||
@@ -79,7 +79,7 @@ class ChatListRecentPeersListItemNode: ListViewItemNode {
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.separatorNode)
|
||||
|
||||
@@ -36,6 +36,10 @@ import MultiAnimationRenderer
|
||||
import PremiumUI
|
||||
import AvatarNode
|
||||
import StoryContainerScreen
|
||||
import ChatListSearchFiltersContainerNode
|
||||
import EdgeEffect
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
|
||||
private enum ChatListTokenId: Int32 {
|
||||
case archive
|
||||
@@ -107,8 +111,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
var dismissSearch: (() -> Void)?
|
||||
var openAdInfo: ((ASDisplayNode, AdPeer) -> Void)?
|
||||
|
||||
private let dimNode: ASDisplayNode
|
||||
let filterContainerNode: ChatListSearchFiltersContainerNode
|
||||
private let edgeEffectView: EdgeEffectView
|
||||
|
||||
private let filterContainerNode: ChatListSearchFiltersContainerNode
|
||||
private let paneContainerNode: ChatListSearchPaneContainerNode
|
||||
private var selectionPanelNode: ChatListSearchMessageSelectionPanelNode?
|
||||
|
||||
@@ -181,9 +186,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
self.openMessage = originalOpenMessage
|
||||
self.present = present
|
||||
self.presentInGlobalOverlay = presentInGlobalOverlay
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5)
|
||||
|
||||
self.edgeEffectView = EdgeEffectView()
|
||||
|
||||
self.filterContainerNode = ChatListSearchFiltersContainerNode()
|
||||
self.paneContainerNode = ChatListSearchPaneContainerNode(context: context, animationCache: animationCache, animationRenderer: animationRenderer, updatedPresentationData: updatedPresentationData, peersFilter: self.peersFilter, requestPeerType: self.requestPeerType, location: location, searchQuery: self.searchQuery.get(), searchOptions: self.searchOptions.get(), navigationController: navigationController, parentController: parentController())
|
||||
@@ -193,7 +197,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
|
||||
self.backgroundColor = filter.contains(.excludeRecent) ? nil : self.presentationData.theme.chatList.backgroundColor
|
||||
|
||||
// self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.paneContainerNode)
|
||||
|
||||
let interaction = ChatListSearchInteraction(openPeer: { peer, chatPeer, threadId, value in
|
||||
@@ -325,6 +328,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
parentController()?.view.endEditing(true)
|
||||
}
|
||||
|
||||
self.view.addSubview(self.edgeEffectView)
|
||||
|
||||
self.addSubnode(self.filterContainerNode)
|
||||
self.filterContainerNode.filterPressed = { [weak self] filter in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@@ -553,9 +559,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
|
||||
public override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
|
||||
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||
}
|
||||
|
||||
public override var hasDim: Bool {
|
||||
@@ -705,18 +708,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
self.transitionFraction = transitionFraction
|
||||
|
||||
if let (layout, _) = self.validLayout {
|
||||
let filters: [ChatListSearchFilter]
|
||||
if let suggestedFilters = self.suggestedFilters, !suggestedFilters.isEmpty {
|
||||
filters = suggestedFilters
|
||||
} else {
|
||||
var isForum = false
|
||||
if case .forum = self.location {
|
||||
isForum = true
|
||||
}
|
||||
|
||||
filters = defaultAvailableSearchPanes(isForum: isForum, hasDownloads: !isForum && self.hasDownloads, hasPublicPosts: self.showPublicPostsTab).map(\.filter)
|
||||
}
|
||||
self.filterContainerNode.update(size: CGSize(width: layout.size.width - 40.0, height: 38.0), sideInset: layout.safeInsets.left - 20.0, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: self.displayGlobalPostsNewBadge, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: transition)
|
||||
self.updateFilterContainerNode(layout: layout, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,18 +754,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
self.cancel?()
|
||||
}
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
|
||||
let isFirstTime = self.validLayout == nil
|
||||
self.validLayout = (layout, navigationBarHeight)
|
||||
|
||||
let topInset = navigationBarHeight
|
||||
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset)))
|
||||
transition.updateFrame(node: self.filterContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight + 6.0), size: CGSize(width: layout.size.width, height: 38.0)))
|
||||
|
||||
|
||||
private func updateFilterContainerNode(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
var isForum = false
|
||||
if case .forum = self.location {
|
||||
isForum = true
|
||||
@@ -786,8 +768,46 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
filters = defaultAvailableSearchPanes(isForum: isForum, hasDownloads: self.hasDownloads, hasPublicPosts: self.showPublicPostsTab).map(\.filter)
|
||||
}
|
||||
|
||||
let overflowInset: CGFloat = 20.0
|
||||
self.filterContainerNode.update(size: CGSize(width: layout.size.width - overflowInset * 2.0, height: 38.0), sideInset: layout.safeInsets.left - overflowInset, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: self.displayGlobalPostsNewBadge, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
var filtersInsets = UIEdgeInsets(top: 0.0, left: 12.0, bottom: layout.insets(options: [.input]).bottom + 34.0, right: 12.0)
|
||||
if layout.insets(options: [.input]).bottom <= 30.0 {
|
||||
filtersInsets = ContainerViewLayout.concentricInsets(bottomInset: layout.insets(options: [.input]).bottom, innerDiameter: 40.0, sideInset: 32.0)
|
||||
} else if layout.insets(options: [.input]).bottom <= 84.0 {
|
||||
filtersInsets.left = 20.0
|
||||
filtersInsets.right = filtersInsets.left
|
||||
}
|
||||
|
||||
self.filterContainerNode.update(size: CGSize(width: layout.size.width - (layout.safeInsets.left + filtersInsets.left) * 2.0, height: 40.0), sideInset: 0.0, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: self.displayGlobalPostsNewBadge, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
|
||||
let isFirstTime = self.validLayout == nil
|
||||
self.validLayout = (layout, navigationBarHeight)
|
||||
|
||||
let topInset = navigationBarHeight
|
||||
|
||||
var filtersInsets = UIEdgeInsets(top: 0.0, left: 12.0, bottom: layout.insets(options: [.input]).bottom, right: 12.0)
|
||||
if filtersInsets.bottom == 84.0 {
|
||||
filtersInsets.bottom -= 6.0
|
||||
}
|
||||
if layout.insets(options: [.input]).bottom <= 30.0 {
|
||||
filtersInsets = ContainerViewLayout.concentricInsets(bottomInset: layout.insets(options: [.input]).bottom, innerDiameter: 40.0, sideInset: 32.0)
|
||||
} else if layout.insets(options: [.input]).bottom <= 84.0 {
|
||||
filtersInsets.left = 20.0
|
||||
filtersInsets.right = filtersInsets.left
|
||||
} else {
|
||||
if let inputHeight = layout.inputHeight, filtersInsets.bottom == inputHeight {
|
||||
filtersInsets.bottom += 8.0
|
||||
}
|
||||
filtersInsets.bottom = max(8.0, filtersInsets.bottom)
|
||||
}
|
||||
if self.stateValue.selectedMessageIds != nil {
|
||||
filtersInsets.bottom += 48.0
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.filterContainerNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + filtersInsets.left, y: layout.size.height - filtersInsets.bottom - 40.0), size: CGSize(width: layout.size.width - (layout.safeInsets.left + filtersInsets.left) * 2.0, height: 40.0)))
|
||||
self.updateFilterContainerNode(layout: layout, transition: transition)
|
||||
|
||||
if isFirstTime {
|
||||
self.filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
@@ -795,13 +815,13 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
|
||||
var bottomIntrinsicInset = layout.intrinsicInsets.bottom
|
||||
if case .chatList(.root) = self.location {
|
||||
if layout.safeInsets.left > overflowInset {
|
||||
/*if case .chatList(.root) = self.location {
|
||||
if layout.safeInsets.left > 20.0 {
|
||||
bottomIntrinsicInset -= 34.0
|
||||
} else {
|
||||
bottomIntrinsicInset -= 49.0
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
if let selectedMessageIds = self.stateValue.selectedMessageIds {
|
||||
var wasAdded = false
|
||||
@@ -927,7 +947,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
return strongSelf.context.sharedContext.chatAvailableMessageActions(engine: strongSelf.context.engine, accountPeerId: strongSelf.context.account.peerId, messageIds: messageIds, messages: messages, peers: peers)
|
||||
}
|
||||
self.selectionPanelNode = selectionPanelNode
|
||||
self.addSubnode(selectionPanelNode)
|
||||
self.insertSubnode(selectionPanelNode, aboveSubnode: self.filterContainerNode)
|
||||
}
|
||||
selectionPanelNode.selectedMessages = selectedMessageIds
|
||||
|
||||
@@ -948,25 +968,36 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
})
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.paneContainerNode, frame: CGRect(x: 0.0, y: topInset, width: layout.size.width, height: layout.size.height - topInset))
|
||||
transition.updateFrame(node: self.paneContainerNode, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height))
|
||||
|
||||
var bottomInset = layout.intrinsicInsets.bottom
|
||||
if let inputHeight = layout.inputHeight {
|
||||
bottomInset = inputHeight
|
||||
} else if let _ = self.selectionPanelNode {
|
||||
bottomInset = bottomIntrinsicInset
|
||||
} else if case .chatList(.root) = self.location {
|
||||
bottomInset -= bottomIntrinsicInset
|
||||
}
|
||||
bottomInset += 10.0
|
||||
|
||||
let availablePanes: [ChatListSearchPaneKey]
|
||||
var isForum = false
|
||||
if case .forum = self.location {
|
||||
isForum = true
|
||||
}
|
||||
if self.displaySearchFilters {
|
||||
availablePanes = defaultAvailableSearchPanes(isForum: isForum, hasDownloads: self.hasDownloads, hasPublicPosts: self.hasPublicPostsTab)
|
||||
} else {
|
||||
availablePanes = isForum ? [.topics] : [.chats]
|
||||
}
|
||||
|
||||
bottomInset += 44.0
|
||||
|
||||
let edgeEffectHeight: CGFloat = bottomInset + 8.0
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight), size: CGSize(width: layout.size.width, height: edgeEffectHeight))
|
||||
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectHeight, 50.0), transition: ComponentTransition(transition))
|
||||
transition.updateAlpha(layer: self.edgeEffectView.layer, alpha: edgeEffectHeight > 21.0 ? 1.0 : 0.0)
|
||||
|
||||
self.paneContainerNode.update(size: CGSize(width: layout.size.width, height: layout.size.height - topInset), sideInset: layout.safeInsets.left, bottomInset: bottomInset, visibleHeight: layout.size.height - topInset, presentationData: self.presentationData, availablePanes: availablePanes, transition: transition)
|
||||
self.paneContainerNode.update(size: CGSize(width: layout.size.width, height: layout.size.height), sideInset: layout.safeInsets.left, topInset: topInset, bottomInset: bottomInset, visibleHeight: layout.size.height, presentationData: self.presentationData, availablePanes: availablePanes, transition: transition)
|
||||
}
|
||||
|
||||
private var currentMessages: ([EnginePeer.Id: EnginePeer], [EngineMessage.Id: EngineMessage]) {
|
||||
@@ -1325,7 +1356,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
title = strongSelf.presentationData.strings.DownloadList_RemoveFileAlertTitle(Int32(messages.count))
|
||||
text = strongSelf.presentationData.strings.DownloadList_RemoveFileAlertText(Int32(messages.count))
|
||||
|
||||
strongSelf.present?(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [
|
||||
strongSelf.present?(textAlertController(context: strongSelf.context, title: title, text: text, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.DownloadList_RemoveFileAlertRemove, action: {
|
||||
|
||||
@@ -38,6 +38,7 @@ import MultilineTextComponent
|
||||
import ButtonComponent
|
||||
import BundleIconComponent
|
||||
import AnimatedTextComponent
|
||||
import TextFormat
|
||||
|
||||
private enum ChatListRecentEntryStableId: Hashable {
|
||||
case topPeers
|
||||
@@ -1696,7 +1697,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
private var emptyRecentAnimationNode: AnimatedStickerNode?
|
||||
private var emptyRecentAnimationSize = CGSize()
|
||||
|
||||
private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData)?
|
||||
private var currentParams: (size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData)?
|
||||
|
||||
private let ready = Promise<Bool>()
|
||||
private var didSetReady: Bool = false
|
||||
@@ -3495,7 +3496,6 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
self.interaction.openStories?(id, sourceNode.avatarNode)
|
||||
}
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
@@ -4508,7 +4508,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
return
|
||||
}
|
||||
dismissImpl?()
|
||||
if let value = attributes[NSAttributedString.Key(rawValue: "URL")] as? String {
|
||||
if let value = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
if !value.isEmpty {
|
||||
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: value, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: {})
|
||||
} else {
|
||||
@@ -4536,7 +4536,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
)
|
||||
interaction.present(alertController, nil)
|
||||
dismissImpl = { [weak alertController] in
|
||||
alertController?.dismissAnimated()
|
||||
alertController?.dismiss()
|
||||
}
|
||||
},
|
||||
isChannelsTabExpanded: recentItems.isChannelsTabExpanded,
|
||||
@@ -4640,8 +4640,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
self.playlistStateAndType = nil
|
||||
}
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring))
|
||||
if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
self.playlistLocation = playlistStateAndType?.1.playlistLocation
|
||||
@@ -4758,10 +4758,10 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||
func update(size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||
let hadValidLayout = self.currentParams != nil
|
||||
let layoutChanged = self.currentParams?.size != size || self.currentParams?.sideInset != sideInset || self.currentParams?.bottomInset != bottomInset || self.currentParams?.visibleHeight != visibleHeight
|
||||
self.currentParams = (size, sideInset, bottomInset, visibleHeight, presentationData)
|
||||
let layoutChanged = self.currentParams?.size != size || self.currentParams?.sideInset != sideInset || self.currentParams?.topInset != topInset || self.currentParams?.bottomInset != bottomInset || self.currentParams?.visibleHeight != visibleHeight
|
||||
self.currentParams = (size, sideInset, topInset, bottomInset, visibleHeight, presentationData)
|
||||
|
||||
var topPanelHeight: CGFloat = 0.0
|
||||
if let (item, previousItem, nextItem, order, type, _) = self.playlistStateAndType {
|
||||
@@ -5035,9 +5035,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|
||||
transition.updateFrame(node: self.mediaAccessoryPanelContainer, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: MediaNavigationAccessoryHeaderNode.minimizedHeight)))
|
||||
|
||||
let topInset: CGFloat = topPanelHeight
|
||||
let overflowInset: CGFloat = 20.0
|
||||
let insets = UIEdgeInsets(top: topPanelHeight, left: sideInset, bottom: bottomInset, right: sideInset)
|
||||
let topInset: CGFloat = topInset + topPanelHeight
|
||||
let overflowInset: CGFloat = 0.0
|
||||
let insets = UIEdgeInsets(top: topInset + topPanelHeight, left: sideInset, bottom: bottomInset, right: sideInset)
|
||||
|
||||
self.shimmerNode.frame = CGRect(origin: CGPoint(x: overflowInset, y: topInset), size: CGSize(width: size.width - overflowInset * 2.0, height: size.height))
|
||||
self.shimmerNode.update(context: self.context, size: CGSize(width: size.width - overflowInset * 2.0, height: size.height), presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, key: !(self.searchQueryValue?.isEmpty ?? true) && self.key == .media ? .chats : self.key, hasSelection: self.selectedMessages != nil, transition: transition)
|
||||
@@ -5480,8 +5480,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
strongSelf.emptyResultsButtonSubtitleText = nil
|
||||
}
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = strongSelf.currentParams {
|
||||
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring))
|
||||
if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData) = strongSelf.currentParams {
|
||||
strongSelf.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
|
||||
if strongSelf.key == .downloads {
|
||||
@@ -5783,7 +5783,6 @@ public final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
|
||||
}, openStories: { _, _ in
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
|
||||
@@ -15,7 +15,7 @@ protocol ChatListSearchPaneNode: ASDisplayNode {
|
||||
var isReady: Signal<Bool, NoError> { get }
|
||||
var isCurrent: Bool { get set }
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition)
|
||||
func update(size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition)
|
||||
func scrollToTop() -> Bool
|
||||
func cancelPreviewGestures()
|
||||
func transitionNodeForGallery(messageId: EngineMessage.Id, media: EngineMedia) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||
@@ -32,21 +32,21 @@ final class ChatListSearchPaneWrapper {
|
||||
let key: ChatListSearchPaneKey
|
||||
let node: ChatListSearchPaneNode
|
||||
var isAnimatingOut: Bool = false
|
||||
private var appliedParams: (CGSize, CGFloat, CGFloat, CGFloat, PresentationData)?
|
||||
private var appliedParams: (CGSize, CGFloat, CGFloat, CGFloat, CGFloat, PresentationData)?
|
||||
|
||||
init(key: ChatListSearchPaneKey, node: ChatListSearchPaneNode) {
|
||||
self.key = key
|
||||
self.node = node
|
||||
}
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||
if let (currentSize, currentSideInset, currentBottomInset, _, currentPresentationData) = self.appliedParams {
|
||||
if currentSize == size && currentSideInset == sideInset && currentBottomInset == bottomInset && currentPresentationData === presentationData {
|
||||
func update(size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||
if let (currentSize, currentSideInset, currentTopInset, currentBottomInset, _, currentPresentationData) = self.appliedParams {
|
||||
if currentSize == size && currentSideInset == sideInset && currentTopInset == topInset && currentBottomInset == bottomInset && currentPresentationData === presentationData {
|
||||
return
|
||||
}
|
||||
}
|
||||
self.appliedParams = (size, sideInset, bottomInset, visibleHeight, presentationData)
|
||||
self.node.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: synchronous, transition: transition)
|
||||
self.appliedParams = (size, sideInset, topInset, bottomInset, visibleHeight, presentationData)
|
||||
self.node.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: synchronous, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
|
||||
var isAdjacentLoadingEnabled = false
|
||||
|
||||
private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, [ChatListSearchPaneKey])?
|
||||
private var currentParams: (size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, [ChatListSearchPaneKey])?
|
||||
|
||||
private(set) var currentPaneKey: ChatListSearchPaneKey?
|
||||
var pendingSwitchToPaneKey: ChatListSearchPaneKey?
|
||||
@@ -251,8 +251,8 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
if self.currentPanes[key] != nil {
|
||||
self.currentPaneKey = key
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring))
|
||||
if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
|
||||
if case .apps = key {
|
||||
@@ -261,8 +261,8 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
} else if self.pendingSwitchToPaneKey != key {
|
||||
self.pendingSwitchToPaneKey = key
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring))
|
||||
if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
|
||||
if case .apps = key {
|
||||
@@ -275,7 +275,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
super.didLoad()
|
||||
|
||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in
|
||||
guard let strongSelf = self, let (_, _, _, _, _, availablePanes) = strongSelf.currentParams, let currentPaneKey = strongSelf.currentPaneKey, let index = availablePanes.firstIndex(of: currentPaneKey) else {
|
||||
guard let strongSelf = self, let (_, _, _, _, _, _, availablePanes) = strongSelf.currentParams, let currentPaneKey = strongSelf.currentPaneKey, let index = availablePanes.firstIndex(of: currentPaneKey) else {
|
||||
return []
|
||||
}
|
||||
if index == 0 {
|
||||
@@ -321,7 +321,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
|
||||
cancelContextGestures(view: self.view)
|
||||
case .changed:
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
self.isAdjacentLoadingEnabled = true
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
var transitionFraction = translation.x / size.width
|
||||
@@ -332,10 +332,10 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
transitionFraction = max(0.0, transitionFraction)
|
||||
}
|
||||
self.transitionFraction = transitionFraction
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .immediate)
|
||||
self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .immediate)
|
||||
}
|
||||
case .cancelled, .ended:
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
let velocity = recognizer.velocity(in: self.view)
|
||||
var directionIsToRight: Bool?
|
||||
@@ -364,7 +364,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
}
|
||||
}
|
||||
self.transitionFraction = 0.0
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.35, curve: .spring))
|
||||
self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.35, curve: .spring))
|
||||
}
|
||||
default:
|
||||
break
|
||||
@@ -396,7 +396,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, availablePanes: [ChatListSearchPaneKey], transition: ContainedViewLayoutTransition) {
|
||||
func update(size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, availablePanes: [ChatListSearchPaneKey], transition: ContainedViewLayoutTransition) {
|
||||
let previousAvailablePanes = self.currentAvailablePanes ?? []
|
||||
self.currentAvailablePanes = availablePanes
|
||||
|
||||
@@ -430,7 +430,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
currentIndex = nil
|
||||
}
|
||||
|
||||
self.currentParams = (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes)
|
||||
self.currentParams = (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes)
|
||||
|
||||
switch self.location {
|
||||
case .forum, .savedMessagesChats:
|
||||
@@ -489,12 +489,12 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = strongSelf.currentParams {
|
||||
if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = strongSelf.currentParams {
|
||||
var transition: ContainedViewLayoutTransition = .immediate
|
||||
if strongSelf.pendingSwitchToPaneKey == key && strongSelf.currentPaneKey != nil {
|
||||
transition = .animated(duration: 0.4, curve: .spring)
|
||||
}
|
||||
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: transition)
|
||||
strongSelf.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: transition)
|
||||
}
|
||||
}
|
||||
if leftScope {
|
||||
@@ -504,14 +504,14 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
)
|
||||
self.pendingPanes[key] = pane
|
||||
pane.pane.node.frame = paneFrame
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .immediate)
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .immediate)
|
||||
leftScope = true
|
||||
}
|
||||
}
|
||||
|
||||
for (key, pane) in self.pendingPanes {
|
||||
pane.pane.node.frame = paneFrame
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate)
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate)
|
||||
|
||||
if pane.isReady {
|
||||
self.pendingPanes.removeValue(forKey: key)
|
||||
@@ -587,7 +587,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
paneCompletion()
|
||||
})
|
||||
}
|
||||
pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition)
|
||||
pane.update(size: paneFrame.size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition)
|
||||
pane.node.isCurrent = key == self.currentPaneKey
|
||||
if paneWasAdded && key == self.currentPaneKey {
|
||||
pane.node.didBecomeFocused()
|
||||
@@ -598,7 +598,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
for (_, pane) in self.pendingPanes {
|
||||
let paneTransition: ContainedViewLayoutTransition = .immediate
|
||||
paneTransition.updateFrame(node: pane.pane.node, frame: paneFrame)
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: paneTransition)
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: paneTransition)
|
||||
}
|
||||
if !self.didSetIsReady {
|
||||
if let currentPaneKey = self.currentPaneKey, let currentPane = self.currentPanes[currentPaneKey] {
|
||||
|
||||
@@ -157,7 +157,6 @@ public final class ChatListShimmerNode: ASDisplayNode {
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _, _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
|
||||
@@ -126,7 +126,7 @@ public class ItemListFilterTitleInputItemNode: ListViewItemNode, UITextFieldDele
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
|
||||
@@ -172,7 +172,7 @@ class ChatListArchiveInfoItemNode: ListViewItemNode, ASScrollViewDelegate {
|
||||
self.infoPageNodes = (0 ..< 3).map({ _ in InfoPageNode() })
|
||||
self.pageControlNode.pagesCount = self.infoPageNodes.count
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.scrollNode)
|
||||
self.infoPageNodes.forEach(self.scrollNode.addSubnode)
|
||||
|
||||
@@ -55,7 +55,7 @@ class ChatListEmptyHeaderItemNode: ListViewItemNode {
|
||||
private var item: ChatListEmptyHeaderItem?
|
||||
|
||||
required init() {
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
}
|
||||
|
||||
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
|
||||
@@ -92,7 +92,7 @@ class ChatListEmptyInfoItemNode: ListViewItemNode {
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.textNode = TextNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.animationNode)
|
||||
self.addSubnode(self.textNode)
|
||||
@@ -207,7 +207,7 @@ class ChatListSectionHeaderNode: ListViewItemNode {
|
||||
private var headerNode: ListSectionHeaderNode?
|
||||
|
||||
required init() {
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.zPosition = 1.0
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class ChatListHoleItemNode: ListViewItemNode {
|
||||
var relativePosition: (first: Bool, last: Bool) = (false, false)
|
||||
|
||||
required init() {
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
}
|
||||
|
||||
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
@@ -153,7 +153,7 @@ class ChatListSearchEmptyFooterItemNode: ListViewItemNode {
|
||||
self.searchAllMessagesTitle = TextNode()
|
||||
self.searchAllMessagesTitle.isUserInteractionEnabled = false
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
self.contentNode.addSubnode(self.titleNode)
|
||||
|
||||
@@ -15,7 +15,6 @@ import PeerOnlineMarkerNode
|
||||
import LocalizedPeerData
|
||||
import PeerPresenceStatusManager
|
||||
import PhotoResources
|
||||
import ChatListSearchItemNode
|
||||
import ContextUI
|
||||
import ChatInterfaceState
|
||||
import TextFormat
|
||||
@@ -219,6 +218,7 @@ public enum ChatListItemContent {
|
||||
public var message: EngineMessage?
|
||||
public var unreadCount: Int
|
||||
public var hiddenByDefault: Bool
|
||||
public var appearsPinned: Bool
|
||||
public var storyState: StoryState?
|
||||
|
||||
public init(
|
||||
@@ -227,6 +227,7 @@ public enum ChatListItemContent {
|
||||
message: EngineMessage?,
|
||||
unreadCount: Int,
|
||||
hiddenByDefault: Bool,
|
||||
appearsPinned: Bool,
|
||||
storyState: StoryState?
|
||||
) {
|
||||
self.groupId = groupId
|
||||
@@ -234,6 +235,7 @@ public enum ChatListItemContent {
|
||||
self.message = message
|
||||
self.unreadCount = unreadCount
|
||||
self.hiddenByDefault = hiddenByDefault
|
||||
self.appearsPinned = appearsPinned
|
||||
self.storyState = storyState
|
||||
}
|
||||
}
|
||||
@@ -454,7 +456,7 @@ private final class ChatListItemTagListComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
|
||||
public class ChatListItem: ListViewItem {
|
||||
public enum EnabledContextActions {
|
||||
public struct Actions: OptionSet {
|
||||
public var rawValue: Int32
|
||||
@@ -1472,7 +1474,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
|
||||
}
|
||||
let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
|
||||
let (_, initialHideAuthor, messageText, _, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
|
||||
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
|
||||
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
|
||||
}
|
||||
@@ -1506,7 +1508,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
|
||||
}
|
||||
let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: peerData.messages, chatPeer: peerData.peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
|
||||
let (_, initialHideAuthor, messageText, _, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: peerData.messages, chatPeer: peerData.peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
|
||||
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
|
||||
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
|
||||
}
|
||||
@@ -1656,7 +1658,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.isAccessibilityElement = true
|
||||
|
||||
@@ -2452,7 +2454,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let leftInset: CGFloat = params.leftInset + avatarLeftInset
|
||||
|
||||
enum ContentData {
|
||||
case chat(itemPeer: EngineRenderedPeer, threadInfo: ChatListItemContent.ThreadInfo?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?)
|
||||
case chat(itemPeer: EngineRenderedPeer, threadInfo: ChatListItemContent.ThreadInfo?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, messageEntities: [MessageTextEntity], spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?)
|
||||
case group(peers: [EngineChatList.GroupItem.Item])
|
||||
}
|
||||
|
||||
@@ -2461,7 +2463,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
var hideAuthor = false
|
||||
switch contentPeer {
|
||||
case let .chat(itemPeer):
|
||||
var (peer, initialHideAuthor, messageText, spoilers, customEmojiRanges) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup)
|
||||
var (peer, initialHideAuthor, messageText, messageEntities, spoilers, customEmojiRanges) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup)
|
||||
|
||||
if case let .psa(_, maybePsaText) = promoInfo, let psaText = maybePsaText {
|
||||
initialHideAuthor = true
|
||||
@@ -2489,7 +2491,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
break
|
||||
}
|
||||
|
||||
contentData = .chat(itemPeer: itemPeer, threadInfo: threadInfo, peer: peer, hideAuthor: hideAuthor, messageText: messageText, spoilers: spoilers, customEmojiRanges: customEmojiRanges)
|
||||
contentData = .chat(itemPeer: itemPeer, threadInfo: threadInfo, peer: peer, hideAuthor: hideAuthor, messageText: messageText, messageEntities: messageEntities, spoilers: spoilers, customEmojiRanges: customEmojiRanges)
|
||||
hideAuthor = initialHideAuthor
|
||||
case let .group(groupPeers):
|
||||
contentData = .group(peers: groupPeers)
|
||||
@@ -2508,7 +2510,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
forumTopicData = nil
|
||||
topForumTopicItems = []
|
||||
|
||||
if case let .chat(itemPeer, _, _, _, _, _, _) = contentData {
|
||||
if case let .chat(itemPeer, _, _, _, _, _, _, _) = contentData {
|
||||
if let messagePeer = itemPeer.chatMainPeer {
|
||||
switch messagePeer {
|
||||
case let .channel(channel):
|
||||
@@ -2556,7 +2558,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
var ignoreForwardedIcon = false
|
||||
|
||||
switch contentData {
|
||||
case let .chat(itemPeer, _, _, _, text, spoilers, customEmojiRanges):
|
||||
case let .chat(itemPeer, _, _, _, text, entities, spoilers, customEmojiRanges):
|
||||
var isUser = false
|
||||
if case .user = itemPeer.chatMainPeer {
|
||||
isUser = true
|
||||
@@ -2639,7 +2641,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
chatListText = (text, messageText)
|
||||
}
|
||||
|
||||
|
||||
if inlineAuthorPrefix == nil, let mediaDraftContentType {
|
||||
hasDraft = true
|
||||
authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor)
|
||||
@@ -2671,8 +2673,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if let peerText = peerText {
|
||||
authorAttributedString = NSAttributedString(string: peerText, font: textFont, textColor: theme.authorNameColor)
|
||||
}
|
||||
|
||||
var entities = (message._asMessage().textEntitiesAttribute?.entities ?? []).filter { entity in
|
||||
|
||||
var entities = entities.filter { entity in
|
||||
switch entity.type {
|
||||
case .Spoiler, .CustomEmoji:
|
||||
return true
|
||||
@@ -2690,14 +2692,14 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
regex = loginCodeRegex
|
||||
}
|
||||
if let cached = currentCustomTextEntities, cached.matches(text: message.text) {
|
||||
if let cached = currentCustomTextEntities, cached.matches(text: messageText) {
|
||||
customTextEntities = cached
|
||||
} else if let matches = regex?.matches(in: message.text, options: [], range: NSMakeRange(0, (message.text as NSString).length)) {
|
||||
} else if let matches = regex?.matches(in: messageText, options: [], range: NSMakeRange(0, (messageText as NSString).length)) {
|
||||
var entities: [MessageTextEntity] = []
|
||||
if let first = matches.first {
|
||||
entities.append(MessageTextEntity(range: first.range.location ..< first.range.location + first.range.length, type: .Spoiler))
|
||||
}
|
||||
customTextEntities = CachedCustomTextEntities(text: message.text, textEntities: entities)
|
||||
customTextEntities = CachedCustomTextEntities(text: messageText, textEntities: entities)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2706,14 +2708,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
let messageString: NSAttributedString
|
||||
if !message.text.isEmpty && entities.count > 0 {
|
||||
var messageText = message.text
|
||||
var entities = entities
|
||||
if !"".isEmpty, let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, !translation.text.isEmpty {
|
||||
messageText = translation.text
|
||||
entities = translation.entities
|
||||
}
|
||||
|
||||
if !messageText.isEmpty && entities.count > 0 {
|
||||
messageString = foldLineBreaks(stringWithAppliedEntities(messageText, entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage()))
|
||||
} else if spoilers != nil || customEmojiRanges != nil {
|
||||
let mutableString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
|
||||
@@ -3100,7 +3095,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
switch contentData {
|
||||
case let .chat(itemPeer, threadInfo, _, _, _, _, _):
|
||||
case let .chat(itemPeer, threadInfo, _, _, _, _, _, _):
|
||||
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData {
|
||||
if customMessageListData.commandPrefix != nil {
|
||||
titleAttributedString = nil
|
||||
@@ -3854,6 +3849,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
transition = .immediate
|
||||
}
|
||||
|
||||
transition.updateAlpha(node: strongSelf, alpha: item.hiddenOffset ? 0.0 : 1.0)
|
||||
ComponentTransition(transition).setBlur(layer: strongSelf.layer, radius: item.hiddenOffset ? 8.0 : 0.0)
|
||||
|
||||
let contextContainerFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.contentSize.width, height: itemHeight))
|
||||
// strongSelf.contextContainer.position = contextContainerFrame.center
|
||||
transition.updatePosition(node: strongSelf.contextContainer, position: contextContainerFrame.center)
|
||||
@@ -5031,7 +5029,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if case let .groupReference(groupReferenceData) = item.content, groupReferenceData.hiddenByDefault {
|
||||
separatorInset = 0.0
|
||||
} else if (!nextIsPinned && isPinned) || last {
|
||||
separatorInset = 0.0
|
||||
separatorInset = 0.0
|
||||
} else {
|
||||
separatorInset = editingOffset + leftInset + rawContentRect.origin.x
|
||||
}
|
||||
@@ -5057,8 +5055,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
highlightedBackgroundColor = theme.itemHighlightedBackgroundColor
|
||||
} else if isPinned {
|
||||
if case let .groupReference(groupReferenceData) = item.content, groupReferenceData.hiddenByDefault {
|
||||
backgroundColor = theme.itemBackgroundColor
|
||||
highlightedBackgroundColor = theme.itemHighlightedBackgroundColor
|
||||
backgroundColor = groupReferenceData.appearsPinned ? theme.pinnedItemBackgroundColor : theme.itemBackgroundColor
|
||||
highlightedBackgroundColor = groupReferenceData.appearsPinned ? theme.pinnedItemHighlightedBackgroundColor : theme.itemHighlightedBackgroundColor
|
||||
} else {
|
||||
backgroundColor = theme.pinnedItemBackgroundColor
|
||||
highlightedBackgroundColor = theme.pinnedItemHighlightedBackgroundColor
|
||||
|
||||
@@ -77,20 +77,21 @@ private func paidContentGroupType(paidContent: TelegramMediaPaidContent) -> Mess
|
||||
return currentType
|
||||
}
|
||||
|
||||
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, contentSettings: ContentSettings, messages: [EngineMessage], chatPeer: EngineRenderedPeer, accountPeerId: EnginePeer.Id, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) {
|
||||
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, contentSettings: ContentSettings, messages: [EngineMessage], chatPeer: EngineRenderedPeer, accountPeerId: EnginePeer.Id, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: EnginePeer?, hideAuthor: Bool, messageText: String, messageEntities: [MessageTextEntity], spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) {
|
||||
let peer: EnginePeer?
|
||||
|
||||
let message = messages.last
|
||||
|
||||
if let restrictionReason = message?._asMessage().restrictionReason(platform: "ios", contentSettings: contentSettings) {
|
||||
return (nil, false, restrictionReason, nil, nil)
|
||||
return (nil, false, restrictionReason, [], nil, nil)
|
||||
}
|
||||
if let restrictionReason = chatPeer.chatMainPeer?.restrictionText(platform: "ios", contentSettings: contentSettings) {
|
||||
return (nil, false, restrictionReason, nil, nil)
|
||||
return (nil, false, restrictionReason, [], nil, nil)
|
||||
}
|
||||
|
||||
var hideAuthor = false
|
||||
var messageText: String
|
||||
var messageEntities: [MessageTextEntity] = []
|
||||
var spoilers: [NSRange]?
|
||||
var customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?
|
||||
if let message = message {
|
||||
@@ -104,6 +105,7 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
|
||||
for message in messages {
|
||||
if !message.text.isEmpty {
|
||||
messageText = message.text
|
||||
messageEntities = message._asMessage().textEntitiesAttribute?.entities ?? []
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -469,5 +471,5 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
|
||||
}
|
||||
}
|
||||
|
||||
return (peer, hideAuthor, messageText, spoilers, customEmojiRanges)
|
||||
return (peer, hideAuthor, messageText, messageEntities, spoilers, customEmojiRanges)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import ChatListHeaderComponent
|
||||
import UndoUI
|
||||
import NewSessionInfoScreen
|
||||
import PresentationDataUtils
|
||||
import GlobalControlPanelsContext
|
||||
|
||||
public enum ChatListNodeMode {
|
||||
case chatList(appendContacts: Bool)
|
||||
@@ -110,7 +111,6 @@ public final class ChatListNodeInteraction {
|
||||
let hideChatFolderUpdates: () -> Void
|
||||
let openStories: (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void
|
||||
let openStarsTopup: (Int64?) -> Void
|
||||
let dismissNotice: (ChatListNotice) -> Void
|
||||
let editPeer: (ChatListItem) -> Void
|
||||
let openWebApp: (TelegramUser) -> Void
|
||||
let openPhotoSetup: () -> Void
|
||||
@@ -171,7 +171,6 @@ public final class ChatListNodeInteraction {
|
||||
hideChatFolderUpdates: @escaping () -> Void,
|
||||
openStories: @escaping (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void,
|
||||
openStarsTopup: @escaping (Int64?) -> Void,
|
||||
dismissNotice: @escaping (ChatListNotice) -> Void,
|
||||
editPeer: @escaping (ChatListItem) -> Void,
|
||||
openWebApp: @escaping (TelegramUser) -> Void,
|
||||
openPhotoSetup: @escaping () -> Void,
|
||||
@@ -219,7 +218,6 @@ public final class ChatListNodeInteraction {
|
||||
self.hideChatFolderUpdates = hideChatFolderUpdates
|
||||
self.openStories = openStories
|
||||
self.openStarsTopup = openStarsTopup
|
||||
self.dismissNotice = dismissNotice
|
||||
self.editPeer = editPeer
|
||||
self.openWebApp = openWebApp
|
||||
self.openPhotoSetup = openPhotoSetup
|
||||
@@ -698,6 +696,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
message: groupReferenceEntry.message,
|
||||
unreadCount: groupReferenceEntry.unreadCount,
|
||||
hiddenByDefault: groupReferenceEntry.hiddenByDefault,
|
||||
appearsPinned: groupReferenceEntry.appearsPinned,
|
||||
storyState: groupReferenceEntry.storyState.flatMap { storyState in
|
||||
return ChatListItemContent.StoryState(
|
||||
stats: storyState.stats,
|
||||
@@ -751,47 +750,6 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSectionHeaderItem(theme: presentationData.theme, strings: presentationData.strings, hide: displayHide ? {
|
||||
hideChatListContacts(context: context)
|
||||
} : nil), directionHint: entry.directionHint)
|
||||
case let .Notice(presentationData, notice):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListNoticeItem(context: context, theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] action in
|
||||
switch action {
|
||||
case .activate:
|
||||
switch notice {
|
||||
case .clearStorage:
|
||||
nodeInteraction?.openStorageManagement()
|
||||
case .setupPassword:
|
||||
nodeInteraction?.openPasswordSetup()
|
||||
case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore:
|
||||
nodeInteraction?.openPremiumIntro()
|
||||
case .xmasPremiumGift:
|
||||
nodeInteraction?.openPremiumGift([], nil)
|
||||
case .premiumGrace:
|
||||
nodeInteraction?.openPremiumManagement()
|
||||
case .setupBirthday:
|
||||
nodeInteraction?.openBirthdaySetup()
|
||||
case let .birthdayPremiumGift(peers, birthdays):
|
||||
nodeInteraction?.openPremiumGift(peers, birthdays)
|
||||
case .reviewLogin:
|
||||
break
|
||||
case let .starsSubscriptionLowBalance(amount, _):
|
||||
nodeInteraction?.openStarsTopup(amount.value)
|
||||
case .setupPhoto:
|
||||
nodeInteraction?.openPhotoSetup()
|
||||
case .accountFreeze:
|
||||
nodeInteraction?.openAccountFreezeInfo()
|
||||
case let .link(_, url, _, _):
|
||||
nodeInteraction?.openUrl(url)
|
||||
}
|
||||
case .hide:
|
||||
nodeInteraction?.dismissNotice(notice)
|
||||
case let .buttonChoice(isPositive):
|
||||
switch notice {
|
||||
case let .reviewLogin(newSessionReview, _):
|
||||
nodeInteraction?.performActiveSessionAction(newSessionReview, isPositive)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}), directionHint: entry.directionHint)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1048,6 +1006,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
message: groupReferenceEntry.message,
|
||||
unreadCount: groupReferenceEntry.unreadCount,
|
||||
hiddenByDefault: groupReferenceEntry.hiddenByDefault,
|
||||
appearsPinned: groupReferenceEntry.appearsPinned,
|
||||
storyState: groupReferenceEntry.storyState.flatMap { storyState in
|
||||
return ChatListItemContent.StoryState(
|
||||
stats: storyState.stats,
|
||||
@@ -1101,47 +1060,6 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSectionHeaderItem(theme: presentationData.theme, strings: presentationData.strings, hide: displayHide ? {
|
||||
hideChatListContacts(context: context)
|
||||
} : nil), directionHint: entry.directionHint)
|
||||
case let .Notice(presentationData, notice):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListNoticeItem(context: context, theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] action in
|
||||
switch action {
|
||||
case .activate:
|
||||
switch notice {
|
||||
case .clearStorage:
|
||||
nodeInteraction?.openStorageManagement()
|
||||
case .setupPassword:
|
||||
nodeInteraction?.openPasswordSetup()
|
||||
case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore:
|
||||
nodeInteraction?.openPremiumIntro()
|
||||
case .xmasPremiumGift:
|
||||
nodeInteraction?.openPremiumGift([], nil)
|
||||
case .premiumGrace:
|
||||
nodeInteraction?.openPremiumManagement()
|
||||
case .setupBirthday:
|
||||
nodeInteraction?.openBirthdaySetup()
|
||||
case let .birthdayPremiumGift(peers, birthdays):
|
||||
nodeInteraction?.openPremiumGift(peers, birthdays)
|
||||
case .reviewLogin:
|
||||
break
|
||||
case let .starsSubscriptionLowBalance(amount, _):
|
||||
nodeInteraction?.openStarsTopup(amount.value)
|
||||
case .setupPhoto:
|
||||
nodeInteraction?.openPhotoSetup()
|
||||
case .accountFreeze:
|
||||
nodeInteraction?.openAccountFreezeInfo()
|
||||
case let .link(_, url, _, _):
|
||||
nodeInteraction?.openUrl(url)
|
||||
}
|
||||
case .hide:
|
||||
nodeInteraction?.dismissNotice(notice)
|
||||
case let .buttonChoice(isPositive):
|
||||
switch notice {
|
||||
case let .reviewLogin(newSessionReview, _):
|
||||
nodeInteraction?.performActiveSessionAction(newSessionReview, isPositive)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}), directionHint: entry.directionHint)
|
||||
case .HeaderEntry:
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint)
|
||||
case let .AdditionalCategory(index: _, id, title, image, appearance, selected, presentationData):
|
||||
@@ -1282,7 +1200,7 @@ public final class ChatListNode: ListView {
|
||||
return []
|
||||
}
|
||||
}
|
||||
private var interaction: ChatListNodeInteraction?
|
||||
public private(set) var interaction: ChatListNodeInteraction?
|
||||
|
||||
private var dequeuedInitialTransitionOnLayout = false
|
||||
private var enqueuedTransition: (ChatListNodeListViewTransition, () -> Void)?
|
||||
@@ -1383,7 +1301,6 @@ public final class ChatListNode: ListView {
|
||||
private let autoSetReady: Bool
|
||||
|
||||
public let isMainTab = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
private let suggestedChatListNotice = Promise<ChatListNotice?>(nil)
|
||||
|
||||
public var synchronousDrawingWhenNotAnimated: Bool = false
|
||||
|
||||
@@ -1868,38 +1785,6 @@ public final class ChatListNode: ListView {
|
||||
return
|
||||
}
|
||||
self.openStarsTopup?(amount)
|
||||
}, dismissNotice: { [weak self] notice in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
switch notice {
|
||||
case .xmasPremiumGift:
|
||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.xmasPremiumGift.id).startStandalone()
|
||||
self.present?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in
|
||||
return true
|
||||
}))
|
||||
case .setupBirthday:
|
||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupBirthday.id).startStandalone()
|
||||
self.present?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_BirthdayInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in
|
||||
return true
|
||||
}))
|
||||
case .birthdayPremiumGift:
|
||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.todayBirthdays.id).startStandalone()
|
||||
self.present?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in
|
||||
return true
|
||||
}))
|
||||
case .premiumGrace:
|
||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.gracePremium.id).startStandalone()
|
||||
case .setupPhoto:
|
||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPhoto.id).startStandalone()
|
||||
case .starsSubscriptionLowBalance:
|
||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.starsSubscriptionLowBalance.id).startStandalone()
|
||||
case let .link(id, _, _, _):
|
||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: id).startStandalone()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { [weak self] user in
|
||||
guard let self else {
|
||||
@@ -1992,172 +1877,12 @@ public final class ChatListNode: ListView {
|
||||
} else {
|
||||
displayArchiveIntro = .single(false)
|
||||
}
|
||||
|
||||
let starsSubscriptionsContextPromise = Promise<StarsSubscriptionsContext?>(nil)
|
||||
|
||||
self.updateIsMainTabDisposable = (self.isMainTab.get()
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] isMainTab in
|
||||
guard let self else {
|
||||
return
|
||||
|> deliverOnMainQueue).startStrict(next: { isMainTab in
|
||||
if isMainTab {
|
||||
let _ = context.engine.privacy.cleanupSessionReviews().startStandalone()
|
||||
}
|
||||
|
||||
guard case .chatList(groupId: .root) = location, isMainTab else {
|
||||
self.suggestedChatListNotice.set(.single(nil))
|
||||
return
|
||||
}
|
||||
|
||||
let _ = context.engine.privacy.cleanupSessionReviews().startStandalone()
|
||||
|
||||
let twoStepData: Signal<TwoStepVerificationConfiguration?, NoError> = .single(nil) |> then(context.engine.auth.twoStepVerificationConfiguration() |> map(Optional.init))
|
||||
|
||||
let accountFreezeConfiguration = (context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
|> map { view -> AppConfiguration in
|
||||
let appConfiguration: AppConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue
|
||||
return appConfiguration
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> map { appConfiguration -> AccountFreezeConfiguration in
|
||||
return AccountFreezeConfiguration.with(appConfiguration: appConfiguration)
|
||||
})
|
||||
|
||||
let suggestedChatListNoticeSignal: Signal<ChatListNotice?, NoError> = combineLatest(
|
||||
context.engine.notices.getServerProvidedSuggestions(),
|
||||
context.engine.notices.getServerDismissedSuggestions(),
|
||||
twoStepData,
|
||||
newSessionReviews(postbox: context.account.postbox),
|
||||
context.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.Birthday(id: context.account.peerId)
|
||||
),
|
||||
context.account.stateManager.contactBirthdays,
|
||||
starsSubscriptionsContextPromise.get(),
|
||||
accountFreezeConfiguration
|
||||
)
|
||||
|> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, data, birthdays, starsSubscriptionsContext, accountFreezeConfiguration -> Signal<ChatListNotice?, NoError> in
|
||||
let (accountPeer, birthday) = data
|
||||
|
||||
if let newSessionReview = newSessionReviews.first {
|
||||
return .single(.reviewLogin(newSessionReview: newSessionReview, totalCount: newSessionReviews.count))
|
||||
}
|
||||
if suggestions.contains(.setupPassword), let configuration {
|
||||
var notSet = false
|
||||
switch configuration {
|
||||
case let .notSet(pendingEmail):
|
||||
if pendingEmail == nil {
|
||||
notSet = true
|
||||
}
|
||||
case .set:
|
||||
break
|
||||
}
|
||||
if notSet {
|
||||
return .single(.setupPassword)
|
||||
}
|
||||
}
|
||||
|
||||
let today = Calendar(identifier: .gregorian).component(.day, from: Date())
|
||||
var todayBirthdayPeerIds: [EnginePeer.Id] = []
|
||||
for (peerId, birthday) in birthdays {
|
||||
if birthday.day == today {
|
||||
todayBirthdayPeerIds.append(peerId)
|
||||
}
|
||||
}
|
||||
todayBirthdayPeerIds.sort { lhs, rhs in
|
||||
return lhs < rhs
|
||||
}
|
||||
|
||||
if dismissedSuggestions.contains(ServerProvidedSuggestion.todayBirthdays.id) {
|
||||
todayBirthdayPeerIds = []
|
||||
}
|
||||
|
||||
if let _ = accountFreezeConfiguration.freezeUntilDate {
|
||||
return .single(.accountFreeze)
|
||||
} else if suggestions.contains(.starsSubscriptionLowBalance) {
|
||||
if let starsSubscriptionsContext {
|
||||
return starsSubscriptionsContext.state
|
||||
|> map { state in
|
||||
if state.balance > StarsAmount.zero && !state.subscriptions.isEmpty {
|
||||
return .starsSubscriptionLowBalance(
|
||||
amount: state.balance,
|
||||
peers: state.subscriptions.map { $0.peer }
|
||||
)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
starsSubscriptionsContextPromise.set(.single(context.engine.payments.peerStarsSubscriptionsContext(starsContext: nil, missingBalance: true)))
|
||||
return .single(nil)
|
||||
}
|
||||
} else if suggestions.contains(.setupPhoto), let accountPeer, accountPeer.smallProfileImage == nil {
|
||||
return .single(.setupPhoto(accountPeer))
|
||||
} else if suggestions.contains(.gracePremium) {
|
||||
return .single(.premiumGrace)
|
||||
} else if suggestions.contains(.xmasPremiumGift) {
|
||||
return .single(.xmasPremiumGift)
|
||||
} else if suggestions.contains(.annualPremium) || suggestions.contains(.upgradePremium) || suggestions.contains(.restorePremium), let inAppPurchaseManager = context.inAppPurchaseManager {
|
||||
return inAppPurchaseManager.availableProducts
|
||||
|> map { products -> ChatListNotice? in
|
||||
if products.count > 1 {
|
||||
let shortestOptionPrice: (Int64, NSDecimalNumber)
|
||||
if let product = products.first(where: { $0.id.hasSuffix(".monthly") }) {
|
||||
shortestOptionPrice = (Int64(Float(product.priceCurrencyAndAmount.amount)), product.priceValue)
|
||||
} else {
|
||||
shortestOptionPrice = (1, NSDecimalNumber(decimal: 1))
|
||||
}
|
||||
for product in products {
|
||||
if product.id.hasSuffix(".annual") {
|
||||
let fraction = Float(product.priceCurrencyAndAmount.amount) / Float(12) / Float(shortestOptionPrice.0)
|
||||
let discount = Int32(round((1.0 - fraction) * 20.0) * 5.0)
|
||||
if discount > 0 {
|
||||
if suggestions.contains(.restorePremium) {
|
||||
return .premiumRestore(discount: discount)
|
||||
} else if suggestions.contains(.annualPremium) {
|
||||
return .premiumAnnualDiscount(discount: discount)
|
||||
} else if suggestions.contains(.upgradePremium) {
|
||||
return .premiumUpgrade(discount: discount)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
if !GlobalExperimentalSettings.isAppStoreBuild {
|
||||
if suggestions.contains(.restorePremium) {
|
||||
return .premiumRestore(discount: 0)
|
||||
} else if suggestions.contains(.annualPremium) {
|
||||
return .premiumAnnualDiscount(discount: 0)
|
||||
} else if suggestions.contains(.upgradePremium) {
|
||||
return .premiumUpgrade(discount: 0)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else if !todayBirthdayPeerIds.isEmpty {
|
||||
return context.engine.data.get(
|
||||
EngineDataMap(todayBirthdayPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))
|
||||
)
|
||||
|> map { result -> ChatListNotice? in
|
||||
var todayBirthdayPeers: [EnginePeer] = []
|
||||
for (peerId, _) in birthdays {
|
||||
if let maybePeer = result[peerId], let peer = maybePeer {
|
||||
todayBirthdayPeers.append(peer)
|
||||
}
|
||||
}
|
||||
return .birthdayPremiumGift(peers: todayBirthdayPeers, birthdays: birthdays)
|
||||
}
|
||||
} else if suggestions.contains(.setupBirthday) && birthday == nil {
|
||||
return .single(.setupBirthday)
|
||||
} else if case let .link(id, url, title, subtitle) = suggestions.first(where: { if case .link = $0 { return true } else { return false} }) {
|
||||
return .single(.link(id: id, url: url, title: title, subtitle: subtitle))
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
self.suggestedChatListNotice.set(suggestedChatListNoticeSignal)
|
||||
}).strict()
|
||||
|
||||
let storageInfo: Signal<Double?, NoError>
|
||||
@@ -2346,7 +2071,6 @@ public final class ChatListNode: ListView {
|
||||
hideArchivedFolderByDefault,
|
||||
displayArchiveIntro,
|
||||
storageInfo,
|
||||
suggestedChatListNotice.get(),
|
||||
savedMessagesPeer,
|
||||
chatListViewUpdate,
|
||||
self.statePromise.get(),
|
||||
@@ -2354,23 +2078,14 @@ public final class ChatListNode: ListView {
|
||||
chatListFilters,
|
||||
accountIsPremium
|
||||
)
|
||||
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, suggestedChatListNotice, savedMessagesPeer, updateAndFilter, state, contacts, chatListFilters, accountIsPremium) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, savedMessagesPeer, updateAndFilter, state, contacts, chatListFilters, accountIsPremium) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||
let (update, filter) = updateAndFilter
|
||||
|
||||
let previousHideArchivedFolderByDefaultValue = previousHideArchivedFolderByDefault.swap(hideArchivedFolderByDefault)
|
||||
|
||||
let notice: ChatListNotice?
|
||||
if let suggestedChatListNotice {
|
||||
notice = suggestedChatListNotice
|
||||
} else if let storageInfo {
|
||||
notice = .clearStorage(sizeFraction: storageInfo)
|
||||
} else {
|
||||
notice = nil
|
||||
}
|
||||
|
||||
let innerIsMainTab = location == .chatList(groupId: .root) && chatListFilter == nil
|
||||
|
||||
let (rawEntries, isLoading) = chatListNodeEntriesForView(view: update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, notice: notice, mode: mode, chatListLocation: location, contacts: contacts, accountPeerId: accountPeerId, isMainTab: innerIsMainTab)
|
||||
let (rawEntries, isLoading) = chatListNodeEntriesForView(view: update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, mode: mode, chatListLocation: location, contacts: contacts, accountPeerId: accountPeerId, isMainTab: innerIsMainTab)
|
||||
var isEmpty = true
|
||||
var entries = rawEntries.filter { entry in
|
||||
switch entry {
|
||||
@@ -2697,7 +2412,6 @@ public final class ChatListNode: ListView {
|
||||
var didIncludeRemovingPeerId = false
|
||||
var didIncludeHiddenByDefaultArchive = false
|
||||
var didIncludeHiddenThread = false
|
||||
var didIncludeNotice = false
|
||||
if let previous = previousView {
|
||||
for entry in previous.filteredEntries {
|
||||
if case let .PeerEntry(peerEntry) = entry {
|
||||
@@ -2724,15 +2438,12 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
} else if case let .GroupReferenceEntry(groupReferenceEntry) = entry {
|
||||
didIncludeHiddenByDefaultArchive = groupReferenceEntry.hiddenByDefault
|
||||
} else if case .Notice = entry {
|
||||
didIncludeNotice = true
|
||||
}
|
||||
}
|
||||
}
|
||||
var doesIncludeRemovingPeerId = false
|
||||
var doesIncludeArchive = false
|
||||
var doesIncludeHiddenByDefaultArchive = false
|
||||
var doesIncludeNotice = false
|
||||
|
||||
var doesIncludeHiddenThread = false
|
||||
for entry in processedView.filteredEntries {
|
||||
@@ -2761,8 +2472,6 @@ public final class ChatListNode: ListView {
|
||||
} else if case let .GroupReferenceEntry(groupReferenceEntry) = entry {
|
||||
doesIncludeArchive = true
|
||||
doesIncludeHiddenByDefaultArchive = groupReferenceEntry.hiddenByDefault
|
||||
} else if case .Notice = entry {
|
||||
doesIncludeNotice = true
|
||||
}
|
||||
}
|
||||
if previousPinnedChats != updatedPinnedChats || previousPinnedThreads != updatedPinnedThreads {
|
||||
@@ -2789,9 +2498,6 @@ public final class ChatListNode: ListView {
|
||||
if didIncludeHiddenThread != doesIncludeHiddenThread {
|
||||
disableAnimations = false
|
||||
}
|
||||
if didIncludeNotice != doesIncludeNotice {
|
||||
disableAnimations = false
|
||||
}
|
||||
}
|
||||
|
||||
if let _ = previousHideArchivedFolderByDefaultValue, previousHideArchivedFolderByDefaultValue != hideArchivedFolderByDefault {
|
||||
@@ -3547,8 +3253,10 @@ public final class ChatListNode: ListView {
|
||||
if entryCount - 1 - i < 0 {
|
||||
continue
|
||||
}
|
||||
if case .PeerEntry = transition.chatListView.filteredEntries[entryCount - 1 - i] {
|
||||
} else {
|
||||
switch transition.chatListView.filteredEntries[entryCount - 1 - i] {
|
||||
case .PeerEntry, .GroupReferenceEntry:
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if case let .index(index) = transition.chatListView.filteredEntries[entryCount - 1 - i].sortIndex, case let .chatList(chatListIndex) = index, chatListIndex.pinningIndex != nil {
|
||||
@@ -3658,7 +3366,7 @@ public final class ChatListNode: ListView {
|
||||
} else {
|
||||
break loop
|
||||
}
|
||||
case .ArchiveIntro, .EmptyIntro, .SectionHeader, .Notice, .HeaderEntry, .AdditionalCategory:
|
||||
case .ArchiveIntro, .EmptyIntro, .SectionHeader, .HeaderEntry, .AdditionalCategory:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,23 +79,6 @@ public enum ChatListNodeEntryPromoInfo: Equatable {
|
||||
case psa(type: String, message: String?)
|
||||
}
|
||||
|
||||
public enum ChatListNotice: Equatable {
|
||||
case clearStorage(sizeFraction: Double)
|
||||
case setupPassword
|
||||
case premiumUpgrade(discount: Int32)
|
||||
case premiumAnnualDiscount(discount: Int32)
|
||||
case premiumRestore(discount: Int32)
|
||||
case xmasPremiumGift
|
||||
case setupBirthday
|
||||
case birthdayPremiumGift(peers: [EnginePeer], birthdays: [EnginePeer.Id: TelegramBirthday])
|
||||
case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int)
|
||||
case premiumGrace
|
||||
case starsSubscriptionLowBalance(amount: StarsAmount, peers: [EnginePeer])
|
||||
case setupPhoto(EnginePeer)
|
||||
case accountFreeze
|
||||
case link(id: String, url: String, title: ServerSuggestionInfo.Item.Text, subtitle: ServerSuggestionInfo.Item.Text)
|
||||
}
|
||||
|
||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
struct PeerEntryData: Equatable {
|
||||
var index: EngineChatList.Item.Index
|
||||
@@ -339,6 +322,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
var unreadCount: Int
|
||||
var revealed: Bool
|
||||
var hiddenByDefault: Bool
|
||||
var appearsPinned: Bool
|
||||
var storyState: ChatListNodeState.StoryState?
|
||||
|
||||
init(
|
||||
@@ -351,6 +335,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
unreadCount: Int,
|
||||
revealed: Bool,
|
||||
hiddenByDefault: Bool,
|
||||
appearsPinned: Bool,
|
||||
storyState: ChatListNodeState.StoryState?
|
||||
) {
|
||||
self.index = index
|
||||
@@ -362,6 +347,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
self.unreadCount = unreadCount
|
||||
self.revealed = revealed
|
||||
self.hiddenByDefault = hiddenByDefault
|
||||
self.appearsPinned = appearsPinned
|
||||
self.storyState = storyState
|
||||
}
|
||||
|
||||
@@ -393,6 +379,9 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
if lhs.hiddenByDefault != rhs.hiddenByDefault {
|
||||
return false
|
||||
}
|
||||
if lhs.appearsPinned != rhs.appearsPinned {
|
||||
return false
|
||||
}
|
||||
if lhs.storyState != rhs.storyState {
|
||||
return false
|
||||
}
|
||||
@@ -409,7 +398,6 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
case ArchiveIntro(presentationData: ChatListPresentationData)
|
||||
case EmptyIntro(presentationData: ChatListPresentationData)
|
||||
case SectionHeader(presentationData: ChatListPresentationData, displayHide: Bool)
|
||||
case Notice(presentationData: ChatListPresentationData, notice: ChatListNotice)
|
||||
case AdditionalCategory(index: Int, id: Int, title: String, image: UIImage?, appearance: ChatListNodeAdditionalCategory.Appearance, selected: Bool, presentationData: ChatListPresentationData)
|
||||
|
||||
var sortIndex: ChatListNodeEntrySortIndex {
|
||||
@@ -430,8 +418,6 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor))
|
||||
case .SectionHeader:
|
||||
return .sectionHeader
|
||||
case .Notice:
|
||||
return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor.successor))
|
||||
case let .AdditionalCategory(index, _, _, _, _, _, _):
|
||||
return .additionalCategory(index)
|
||||
}
|
||||
@@ -460,8 +446,6 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
return .EmptyIntro
|
||||
case .SectionHeader:
|
||||
return .SectionHeader
|
||||
case .Notice:
|
||||
return .Notice
|
||||
case let .AdditionalCategory(_, id, _, _, _, _, _):
|
||||
return .additionalCategory(id)
|
||||
}
|
||||
@@ -534,18 +518,6 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .Notice(lhsPresentationData, lhsInfo):
|
||||
if case let .Notice(rhsPresentationData, rhsInfo) = rhs {
|
||||
if lhsPresentationData !== rhsPresentationData {
|
||||
return false
|
||||
}
|
||||
if lhsInfo != rhsInfo {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .AdditionalCategory(lhsIndex, lhsId, lhsTitle, lhsImage, lhsAppearance, lhsSelected, lhsPresentationData):
|
||||
if case let .AdditionalCategory(rhsIndex, rhsId, rhsTitle, rhsImage, rhsAppearance, rhsSelected, rhsPresentationData) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
@@ -595,7 +567,7 @@ struct ChatListContactPeer {
|
||||
}
|
||||
}
|
||||
|
||||
func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, notice: ChatListNotice?, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation, contacts: [ChatListContactPeer], accountPeerId: EnginePeer.Id, isMainTab: Bool) -> (entries: [ChatListNodeEntry], loading: Bool) {
|
||||
func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation, contacts: [ChatListContactPeer], accountPeerId: EnginePeer.Id, isMainTab: Bool) -> (entries: [ChatListNodeEntry], loading: Bool) {
|
||||
var groupItems = view.groupItems
|
||||
if isMainTab && state.archiveStoryState != nil && groupItems.isEmpty {
|
||||
groupItems.append(EngineChatList.GroupItem(
|
||||
@@ -656,6 +628,8 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
|
||||
|
||||
var hiddenGeneralThread: ChatListNodeEntry?
|
||||
|
||||
var hasPinned = false
|
||||
|
||||
loop: for entry in view.items {
|
||||
var peerId: EnginePeer.Id?
|
||||
var threadId: Int64?
|
||||
@@ -707,6 +681,17 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
|
||||
if let threadData = entry.threadData, let threadId {
|
||||
threadInfo = ChatListItemContent.ThreadInfo(id: threadId, info: threadData.info, isOwnedByMe: threadData.isOwnedByMe, isClosed: threadData.isClosed, isHidden: threadData.isHidden, threadPeer: nil)
|
||||
}
|
||||
|
||||
switch entry.index {
|
||||
case let .chatList(chatList):
|
||||
if chatList.pinningIndex != nil {
|
||||
hasPinned = true
|
||||
}
|
||||
case let .forum(pinnedIndex, _, _, _, _):
|
||||
if case .index = pinnedIndex {
|
||||
hasPinned = true
|
||||
}
|
||||
}
|
||||
|
||||
let entry: ChatListNodeEntry = .PeerEntry(ChatListNodeEntry.PeerEntryData(
|
||||
index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset),
|
||||
@@ -796,6 +781,7 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
|
||||
)))
|
||||
if foundPinningIndex != 0 {
|
||||
foundPinningIndex -= 1
|
||||
hasPinned = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -886,6 +872,7 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
|
||||
)))
|
||||
if pinningIndex != 0 {
|
||||
pinningIndex -= 1
|
||||
hasPinned = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -908,10 +895,12 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
|
||||
unreadCount: groupReference.unreadCount,
|
||||
revealed: state.hiddenItemShouldBeTemporaryRevealed,
|
||||
hiddenByDefault: hideArchivedFolderByDefault,
|
||||
appearsPinned: hasPinned,
|
||||
storyState: mappedStoryState
|
||||
)))
|
||||
if pinningIndex != 0 {
|
||||
pinningIndex -= 1
|
||||
hasPinned = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,10 +916,6 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
|
||||
result.append(.EmptyIntro(presentationData: state.presentationData))
|
||||
}
|
||||
|
||||
if let notice {
|
||||
result.append(.Notice(presentationData: state.presentationData, notice: notice))
|
||||
}
|
||||
|
||||
result.append(.HeaderEntry)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user