Update Ghostgram features

This commit is contained in:
ichmagmaus 812
2026-03-07 18:15:32 +01:00
parent 32b0dddcd9
commit 13a43b9068
902 changed files with 148302 additions and 62355 deletions
+17 -9
View File
@@ -1,20 +1,24 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
sgdeps = [
"//Swiftgram/SGSimpleSettings:SGSimpleSettings",
"//Swiftgram/SGAPIWebSettings:SGAPIWebSettings",
"//Swiftgram/SGAPIToken:SGAPIToken"
]
sgsrcs = [
"//Swiftgram/SGChatListSimpleSettingsSignal:SGChatListSimpleSettingsSignal",
]
swift_library(
name = "ChatListUI",
module_name = "ChatListUI",
srcs = glob(
[
"Sources/**/*.swift",
],
exclude = [
"Sources/ChatListFilterTabContainerNode.swift",
],
),
srcs = glob([
"Sources/**/*.swift",
]) + sgsrcs,
copts = [
"-warnings-as-errors",
],
deps = [
deps = sgdeps + [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
@@ -130,6 +134,10 @@ swift_library(
"//submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent",
"//submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode",
"//submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent",
"//submodules/TelegramUI/Components/AlertComponent",
"//submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent",
"//submodules/TelegramUI/Components/AvatarComponent",
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
],
visibility = [
"//visibility:public",
@@ -0,0 +1,63 @@
import Foundation
import UIKit
import ObjectiveC.runtime
import Display
import ComponentFlow
import Postbox
import TelegramCore
import TelegramPresentationData
import AccountContext
import ContextUI
import ChatListHeaderComponent
private var chatListNodePinnedHeaderDisplayFractionUpdatedKey: UInt8 = 0
private var chatListNodePinnedScrollFractionKey: UInt8 = 0
extension ChatListNavigationBar.View {
func openEmojiStatusSetup() {
}
func updateEdgeEffectForPinnedFraction(pinnedFraction: CGFloat, transition: ComponentTransition) {
}
}
extension ChatListNode {
var pinnedHeaderDisplayFractionUpdated: ((ContainedViewLayoutTransition) -> Void)? {
get {
return objc_getAssociatedObject(self, &chatListNodePinnedHeaderDisplayFractionUpdatedKey) as? ((ContainedViewLayoutTransition) -> Void)
}
set {
objc_setAssociatedObject(self, &chatListNodePinnedHeaderDisplayFractionUpdatedKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
var pinnedScrollFraction: CGFloat {
get {
return CGFloat((objc_getAssociatedObject(self, &chatListNodePinnedScrollFractionKey) as? NSNumber)?.doubleValue ?? 0.0)
}
set {
objc_setAssociatedObject(self, &chatListNodePinnedScrollFractionKey, NSNumber(value: Double(newValue)), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
extension ChatListNodeInteraction {
func openSGAnnouncement(_ id: String, _ url: String, _ needAuth: Bool, _ permanent: Bool) {
self.openUrl(url)
}
}
extension PeerInfoScreen {
public func tabBarItemContextActionRawUIView(sourceView: UIView, gesture: ContextGesture?) {
guard let sourceView = sourceView as? ContextExtractedContentContainingView, let gesture else {
return
}
self.tabBarItemContextAction(sourceView: sourceView, gesture: gesture)
}
}
extension ChatListControllerImpl {
func presentLeaveChannelConfirmation(peer: EnginePeer, nextCreator: EnginePeer, completion: @escaping (Bool) -> Void) {
completion(true)
}
}
@@ -1,3 +1,6 @@
// MARK: Swiftgram
import SGSimpleSettings
import Foundation
import UIKit
import Postbox
@@ -144,7 +147,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
private var didSuggestLoginEmailSetup = false
private var didSuggestLoginPasskeySetup = false
private var presentationData: PresentationData
private(set) var presentationData: PresentationData
private let presentationDataValue = Promise<PresentationData>()
private var presentationDataDisposable: Disposable?
@@ -393,12 +396,23 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
strongSelf.chatListDisplayNode.willScrollToTop()
strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.scrollToPosition(.top(adjustForTempInset: false))
case let .known(offset):
let isFirstFilter = strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.chatListFilter == strongSelf.chatListDisplayNode.mainContainerNode.availableFilters.first?.filter
// MARK: Swiftgram
let sgAllChatsHiddden = SGSimpleSettings.shared.allChatsHidden
var mainContainerNode_availableFilters = strongSelf.chatListDisplayNode.mainContainerNode.availableFilters
if sgAllChatsHiddden {
mainContainerNode_availableFilters.removeAll { $0 == .all }
}
let isFirstFilter = strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.chatListFilter == mainContainerNode_availableFilters.first?.filter
if offset <= ChatListNavigationBar.searchScrollHeight + 1.0 && strongSelf.chatListDisplayNode.inlineStackContainerNode != nil {
strongSelf.setInlineChatList(location: nil)
} else if offset <= ChatListNavigationBar.searchScrollHeight + 1.0 && !isFirstFilter {
let firstFilter = strongSelf.chatListDisplayNode.effectiveContainerNode.availableFilters.first ?? .all
// MARK: Swiftgram
var effectiveContainerNode_availableFilters = strongSelf.chatListDisplayNode.mainContainerNode.availableFilters
if sgAllChatsHiddden {
effectiveContainerNode_availableFilters.removeAll { $0 == .all }
}
let firstFilter = effectiveContainerNode_availableFilters.first ?? .all
let targetTab: ChatListFilterTabEntryId
switch firstFilter {
case .all:
@@ -735,6 +749,22 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if let navigationBarView = strongSelf.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View, let headerPanelsView = navigationBarView.headerPanels as? HeaderPanelContainerComponent.View, let tabsView = headerPanelsView.tabs as? HorizontalTabsComponent.View {
tabsView.updateTabSwitchFraction(fraction: fraction, isDragging: strongSelf.chatListDisplayNode.mainContainerNode.isSwitchingCurrentItemFilterByDragging, transition: ComponentTransition(transition))
}
// MARK: Swiftgram
let switchingToFilterId: Int32
switch (filter) {
case let .filter(filterId):
switchingToFilterId = filterId
default:
switchingToFilterId = -1
}
if fraction.isZero {
let accountId = "\(strongSelf.context.account.peerId.id._internalGetInt64Value())"
if SGSimpleSettings.shared.lastAccountFolders[accountId] != switchingToFilterId {
SGSimpleSettings.shared.lastAccountFolders[accountId] = switchingToFilterId
}
}
}
self.reloadFilters()
}
@@ -1285,10 +1315,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
if let sourceNode {
let controller = ContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, sourceView: sourceView, keepInPlace: keepInPlace)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
let controller = makeContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, sourceView: sourceView, keepInPlace: keepInPlace)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
} else if let sourceView {
let controller = ContextController(presentationData: self.presentationData, source: .reference(ChatListHeaderBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
let controller = makeContextController(presentationData: self.presentationData, source: .reference(ChatListHeaderBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
}
})
@@ -1873,7 +1903,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
case let .groupReference(groupReference):
let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .chatList(groupId: groupReference.groupId), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
chatListController.navigationPresentation = .master
let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupReference.groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupReference.groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
case let .peer(peerData):
let peer = peerData.peer
@@ -1891,12 +1921,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
chatController.canReadHistory.set(false)
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
let contextController = ContextController(presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: nil, isClosed: nil, chatListController: strongSelf, joined: joined, canSelect: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: nil, isClosed: nil, chatListController: strongSelf, joined: joined, canSelect: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
} else {
let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
chatListController.navigationPresentation = .master
let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
} else if let peer = peer.peer, peer.id == strongSelf.context.account.peerId, peerData.displayAsTopicList {
@@ -1908,7 +1938,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
source = .controller(ContextControllerContentSourceImpl(controller: peerInfoController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
}
let contextController = ContextController(presentationData: strongSelf.presentationData, source: source, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: source, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
} else {
@@ -1926,7 +1956,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
}
let contextController = ContextController(context: strongSelf.context, presentationData: strongSelf.presentationData, source: source, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(context: strongSelf.context, presentationData: strongSelf.presentationData, source: source, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
dismissPreviewingImpl = { [weak self, weak contextController] animateIn in
@@ -1960,7 +1990,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
chatController.canReadHistory.set(false)
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
let contextController = ContextController(presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: isPinned, isClosed: threadInfo?.isClosed, chatListController: strongSelf, joined: joined, canSelect: true) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: isPinned, isClosed: threadInfo?.isClosed, chatListController: strongSelf, joined: joined, canSelect: true) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
}
@@ -1995,7 +2025,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if case let .channel(channel) = peer, channel.isForumOrMonoForum {
let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
chatListController.navigationPresentation = .master
let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
} else {
let contextContentSource: ContextContentSource
@@ -2011,7 +2041,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
contextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
}
let contextController = ContextController(context: strongSelf.context, presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(context: strongSelf.context, presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
}
@@ -2117,11 +2147,18 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if self.previewing {
self.storiesReady.set(.single(true))
} else {
self.storySubscriptionsDisposable = (self.context.engine.messages.storySubscriptions(isHidden: self.location == .chatList(groupId: .archive))
|> deliverOnMainQueue).startStrict(next: { [weak self] rawStorySubscriptions in
// MARK: Swiftgram
let hideStoriesSignal = sgSimpleSettingsBoolSignal(.hideStories, defaultValue: false)
self.storySubscriptionsDisposable = (combineLatest(self.context.engine.messages.storySubscriptions(isHidden: self.location == .chatList(groupId: .archive)), hideStoriesSignal)
|> deliverOnMainQueue).startStrict(next: { [weak self] rawStorySubscriptions, hideStories in
guard let self else {
return
}
var rawStorySubscriptions = rawStorySubscriptions
if hideStories {
rawStorySubscriptions = EngineStorySubscriptions(accountItem: nil, items: [], hasMoreToken: nil)
}
self.rawStorySubscriptions = rawStorySubscriptions
var items: [EngineStorySubscriptions.Item] = []
@@ -3029,7 +3066,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface {
let coordinator = rootController.openStoryCamera(customTarget: nil, resumeLiveStream: hasLiveStream, transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut())
let coordinator = rootController.openStoryCamera(mode: .photo, customTarget: nil, resumeLiveStream: hasLiveStream, transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut())
coordinator?.animateIn()
}
}
@@ -3425,7 +3462,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})))
}
let controller = ContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, sourceView: nil, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
let controller = makeContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, sourceView: nil, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
})
}
@@ -3511,7 +3548,17 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
super.navigationStackConfigurationUpdated(next: next)
}
@objc fileprivate func editPressed() {
public func activateEdit() {
self.editPressed()
}
public func openEmojiStatusSetup() {
if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
navigationBarView.openEmojiStatusSetup()
}
}
@objc func editPressed() {
if self.secondaryContext == nil {
if case .chatList(.root) = self.chatListDisplayNode.effectiveContainerNode.location {
self.effectiveContext?.leftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
@@ -3546,6 +3593,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if let layout = self.validLayout {
self.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
}
if SGSimpleSettings.shared.hideTabBar {
(self.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.2, curve: .easeInOut))
}
}
@objc fileprivate func donePressed() {
@@ -3572,6 +3622,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
}
}
if SGSimpleSettings.shared.hideTabBar {
(self.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.2, curve: .easeInOut))
}
}
private var skipTabContainerUpdate = false
@@ -3878,7 +3931,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: sourceController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
let contextController = makeContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: sourceController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
sourceController.presentInGlobalOverlay(contextController)
})
}
@@ -3938,7 +3991,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})))
}
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
let contextController = makeContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
self.presentInGlobalOverlay(contextController)
})
}
@@ -3947,12 +4000,23 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
private func reloadFilters(firstUpdate: (() -> Void)? = nil) {
let filterItems = chatListFilterItems(context: self.context)
var notifiedFirstUpdate = false
// MARK: Swiftgram
let experimentalUISettingsKey: ValueBoxKey = ApplicationSpecificSharedDataKeys.experimentalUISettings
let displayTabsAtBottomSignal = self.context.sharedContext.accountManager.sharedData(keys: Set([experimentalUISettingsKey]))
|> map { sharedData -> Bool in
let settings: ExperimentalUISettings = sharedData.entries[experimentalUISettingsKey]?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
return settings.foldersTabAtBottom
}
|> distinctUntilChanged
self.filterDisposable.set((combineLatest(queue: .mainQueue(),
displayTabsAtBottomSignal,
filterItems,
self.context.account.postbox.peerView(id: self.context.account.peerId),
self.context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false))
)
|> deliverOnMainQueue).startStrict(next: { [weak self] countAndFilterItems, peerView, limits in
|> deliverOnMainQueue).startStrict(next: { [weak self] displayTabsAtBottom, countAndFilterItems, peerView, limits in
guard let strongSelf = self else {
return
}
@@ -3990,13 +4054,19 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
let firstItem = countAndFilterItems.1.first?.0 ?? .allChats
let firstItemEntryId: ChatListFilterTabEntryId
var firstItemEntryId: ChatListFilterTabEntryId
switch firstItem {
case .allChats:
firstItemEntryId = .all
case let .filter(id, _, _, _):
firstItemEntryId = .filter(id)
}
// MARK: Swiftgram
if !strongSelf.initializedFilters && SGSimpleSettings.shared.rememberLastFolder {
if let lastFolder = SGSimpleSettings.shared.lastAccountFolders["\(strongSelf.context.account.peerId.id._internalGetInt64Value())"]{
firstItemEntryId = lastFolder == -1 ? .all : .filter(lastFolder)
}
}
var selectedEntryId = !strongSelf.initializedFilters ? firstItemEntryId : strongSelf.chatListDisplayNode.mainContainerNode.currentItemFilter
var resetCurrentEntry = false
@@ -4021,7 +4091,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
}
let filtersLimit = isPremium == false ? limits.maxFoldersCount : nil
strongSelf.tabContainerData = (resolvedItems, false, filtersLimit)
strongSelf.tabContainerData = (resolvedItems, displayTabsAtBottom, filtersLimit)
var availableFilters: [ChatListContainerNodeFilter] = []
var hasAllChats = false
for item in items {
@@ -4754,7 +4824,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
completion?()
self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: transition)
(self.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: transition)
(self.parent as? TabBarController)?.updateIsTabBarHidden(SGSimpleSettings.shared.hideTabBar ? true : false, transition: transition)
self.isSearchActive = false
if let navigationController = self.navigationController as? NavigationController {
@@ -5421,10 +5491,33 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if canRemoveGlobally && isGroupOrChannel {
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: false, completion: {
})
let proceed = {
self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: false, completion: {
})
}
if let self, case let .channel(channel) = peer.peer, channel.flags.contains(.isCreator) {
let _ = (self.context.engine.peers.getFutureCreatorAfterLeave(peerId: channel.id)
|> deliverOnMainQueue).start(next: { [weak self] nextCreator in
guard let self else {
return
}
if let nextCreator, let peer = peer.peer {
self.presentLeaveChannelConfirmation(peer: peer, nextCreator: nextCreator, completion: { commit in
if commit {
proceed()
}
})
} else {
proceed()
}
})
} else {
proceed()
}
}))
let deleteForAllText: String
@@ -5874,10 +5967,33 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})
], parseMarkdown: true), in: .window(.root))
} else {
completion(true)
self.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
removed()
})
let proceed = {
completion(true)
self.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
removed()
})
}
if case let .channel(channel) = peer.peer, channel.flags.contains(.isCreator) {
let _ = (self.context.engine.peers.getFutureCreatorAfterLeave(peerId: channel.id)
|> deliverOnMainQueue).start(next: { [weak self] nextCreator in
guard let self else {
return
}
if let nextCreator, let peer = peer.peer {
self.presentLeaveChannelConfirmation(peer: peer, nextCreator: nextCreator, completion: { commit in
if commit {
proceed()
} else {
completion(false)
}
})
} else {
proceed()
}
})
} else {
proceed()
}
}
}
@@ -6206,7 +6322,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
}
let controller = ContextController(context: strongSelf.context, presentationData: strongSelf.presentationData, source: .reference(ChatListTabBarContextReferenceContentSource(controller: strongSelf, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
let controller = makeContextController(context: strongSelf.context, presentationData: strongSelf.presentationData, source: .reference(ChatListTabBarContextReferenceContentSource(controller: strongSelf, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
})
}
@@ -6408,7 +6524,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})
})))
let contextController = ContextController(presentationData: presentationData, source: .reference(AdsInfoContextReferenceContentSource(controller: controller, sourceView: referenceView, insets: .zero, contentInsets: .zero)), items: .single(ContextController.Items(content: .list(actions))), gesture: nil)
let contextController = makeContextController(presentationData: presentationData, source: .reference(AdsInfoContextReferenceContentSource(controller: controller, sourceView: referenceView, insets: .zero, contentInsets: .zero)), items: .single(ContextController.Items(content: .list(actions))), gesture: nil)
controller.presentInGlobalOverlay(contextController)
}
@@ -6425,7 +6541,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if let current = self.storyCameraTransitionInCoordinator {
coordinator = current
} else {
coordinator = rootController.openStoryCamera(customTarget: nil, resumeLiveStream: false, transitionIn: nil, transitionedIn: {}, transitionOut: { [weak self] target, _ in
coordinator = rootController.openStoryCamera(mode: .photo, customTarget: nil, resumeLiveStream: false, transitionIn: nil, transitionedIn: {}, transitionOut: { [weak self] target, _ in
guard let self, let target else {
return nil
}
@@ -6591,18 +6707,18 @@ private final class ChatListLocationContext {
private(set) var chatListTitle: NetworkStatusTitle?
var leftButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
var defaultLeftButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
var rightButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
var proxyButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
var storyButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
// GHOSTGRAM: Account switcher liquid glass avatar button for the next account
var accountSwitcherButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
var tabBarMoreButton: AnyComponentWithIdentity<NavigationButtonComponentEnvironment>?
private var accountSwitcherDisposable: Disposable?
private var accountSwitcherAvatarDisposable: Disposable?
private var accountSwitcherInfo: (primary: AccountRecordId?, accounts: [AccountWithInfo])?
var rightButtons: [AnyComponentWithIdentity<NavigationButtonComponentEnvironment>] {
var result: [AnyComponentWithIdentity<NavigationButtonComponentEnvironment>] = []
// Account switcher is first leftmost of the right-side buttons
if let accountSwitcherButton = self.accountSwitcherButton {
result.append(accountSwitcherButton)
}
@@ -6615,6 +6731,9 @@ private final class ChatListLocationContext {
if let proxyButton = self.proxyButton {
result.append(proxyButton)
}
if let tabBarMoreButton = self.tabBarMoreButton {
result.append(tabBarMoreButton)
}
return result
}
@@ -6640,91 +6759,15 @@ private final class ChatListLocationContext {
self.location = location
self.parentController = parentController
// GHOSTGRAM: Subscribe to account list and maintain the switcher button
if case .chatList(.root) = location {
self.accountSwitcherDisposable = (context.sharedContext.activeAccountsWithInfo
|> deliverOnMainQueue)
.start(next: { [weak self] (info: (primary: AccountRecordId?, accounts: [AccountWithInfo])) in
guard let self else { return }
let primaryId = info.primary
let accounts = info.accounts
// Only show when there is more than one account
guard accounts.count > 1, let primaryId = primaryId else {
if self.accountSwitcherButton != nil {
self.accountSwitcherButton = nil
let _ = self.parentController?.updateHeaderContent()
self.parentController?.requestLayout(transition: .immediate)
}
return
}
// Find next account cyclically
let currentIndex = accounts.firstIndex(where: { $0.account.id == primaryId }) ?? 0
let nextIndex = (currentIndex + 1) % accounts.count
let nextAccount = accounts[nextIndex]
let nextPeer = nextAccount.peer
let nextPeerId = "\(nextAccount.account.id)"
// Build button placeholder immediately (image loads async)
let buildButton: (UIImage?) -> Void = { [weak self] image in
guard let self else { return }
guard case .chatList(.root) = self.location else { return }
let sharedContext = self.context.sharedContext
let nextAccountId = nextAccount.account.id
self.accountSwitcherButton = AnyComponentWithIdentity(
id: "accountSwitcher",
component: AnyComponent(NavigationButtonComponent(
content: .avatar(peerId: nextPeerId, avatarImage: image),
pressed: { [weak sharedContext] _ in
sharedContext?.switchToAccount(id: nextAccountId, fromSettingsController: nil, withChatListController: nil)
}
))
)
// Trigger header rebuild
let _ = self.parentController?.updateHeaderContent()
self.parentController?.requestLayout(transition: .immediate)
}
// Attempt to load the peer's avatar from mediaBox
if let representation = nextPeer.smallProfileImage {
self.accountSwitcherAvatarDisposable?.dispose()
let resource = representation.resource
let account = nextAccount.account
// GHOSTGRAM: Fetch first so the resource is populated by the time
// resourceData emits a complete result. The old order (subscribefetch)
// had a race where `completed` fired before data arrived, causing
// buildButton(nil) to be called and the avatar to never show.
if let peerReference = PeerReference(nextPeer) {
let _ = fetchedMediaResource(
mediaBox: account.postbox.mediaBox,
userLocation: .peer(nextPeer.id),
userContentType: .avatar,
reference: .avatar(peer: peerReference, resource: resource)
).start()
}
self.accountSwitcherAvatarDisposable = (account.postbox.mediaBox
.resourceData(resource)
|> filter { $0.complete }
|> take(1)
|> deliverOnMainQueue)
.start(next: { data in
if let uiImage = UIImage(contentsOfFile: data.path) {
buildButton(uiImage)
} else {
buildButton(nil)
}
})
} else {
// No photo show placeholder
buildButton(nil)
}
})
|> deliverOnMainQueue).start(next: { [weak self] info in
guard let self else {
return
}
self.accountSwitcherInfo = info
self.updateAccountSwitcherButton(from: info)
})
}
let hasProxy = context.sharedContext.accountManager.sharedData(keys: [SharedDataKeys.proxySettings])
@@ -6739,6 +6782,9 @@ private final class ChatListLocationContext {
return lhs == rhs
})
// MARK: Swiftgram
let hideStoriesSignal = sgSimpleSettingsBoolSignal(.hideStories, defaultValue: false)
let passcode = context.sharedContext.accountManager.accessChallengeData()
|> map { view -> (Bool, Bool) in
let data = view.data
@@ -6807,6 +6853,7 @@ private final class ChatListLocationContext {
case .chatList:
if !hideNetworkActivityStatus {
self.titleDisposable = combineLatest(queue: .mainQueue(),
hideStoriesSignal,
networkState,
hasProxy,
passcode,
@@ -6815,12 +6862,13 @@ private final class ChatListLocationContext {
peerStatus,
parentController.updatedPresentationData.1,
storyPostingAvailable
).startStrict(next: { [weak self] networkState, proxy, passcode, stateAndFilterId, isReorderingTabs, peerStatus, presentationData, storyPostingAvailable in
).startStrict(next: { [weak self] hideStories, networkState, proxy, passcode, stateAndFilterId, isReorderingTabs, peerStatus, presentationData, storyPostingAvailable in
guard let self else {
return
}
self.updateChatList(
hideStories: hideStories,
networkState: networkState,
proxy: proxy,
passcode: passcode,
@@ -7029,7 +7077,9 @@ private final class ChatListLocationContext {
}
var transition: ContainedViewLayoutTransition = .immediate
let previousToolbar = previousToolbarValue.swap(toolbar)
if (previousToolbar == nil) != (toolbar == nil) {
if SGSimpleSettings.shared.hideTabBar {
transition = .animated(duration: 0.2, curve: .easeInOut)
} else if (previousToolbar == nil) != (toolbar == nil) {
transition = .animated(duration: 0.4, curve: .spring)
}
if strongSelf.toolbar != toolbar {
@@ -7049,7 +7099,87 @@ private final class ChatListLocationContext {
self.accountSwitcherAvatarDisposable?.dispose()
}
private func updateAccountSwitcherButton(from info: (primary: AccountRecordId?, accounts: [AccountWithInfo])) {
let primaryId = info.primary
let accounts = info.accounts
guard accounts.count > 1, let primaryId else {
self.accountSwitcherAvatarDisposable?.dispose()
if self.accountSwitcherButton != nil {
self.accountSwitcherButton = nil
let _ = self.parentController?.updateHeaderContent()
self.parentController?.requestLayout(transition: .immediate)
}
return
}
let currentIndex = accounts.firstIndex(where: { $0.account.id == primaryId }) ?? 0
let targetIndex: Int
if currentIndex == 0 {
targetIndex = min(1, accounts.count - 1)
} else {
targetIndex = currentIndex - 1
}
let targetAccount = accounts[targetIndex]
let targetPeer = targetAccount.peer
let targetPeerId = "\(targetAccount.account.id)"
let buildButton: (UIImage?) -> Void = { [weak self] image in
guard let self else {
return
}
guard case .chatList(.root) = self.location else {
return
}
let sharedContext = self.context.sharedContext
let targetAccountId = targetAccount.account.id
self.accountSwitcherButton = AnyComponentWithIdentity(
id: "accountSwitcher",
component: AnyComponent(NavigationButtonComponent(
content: .avatar(peerId: targetPeerId, avatarImage: image),
pressed: { [weak sharedContext] _ in
sharedContext?.switchToAccount(id: targetAccountId, fromSettingsController: nil, withChatListController: nil)
}
))
)
let _ = self.parentController?.updateHeaderContent()
self.parentController?.requestLayout(transition: .immediate)
}
if let representation = targetPeer.smallProfileImage {
self.accountSwitcherAvatarDisposable?.dispose()
let resource = representation.resource
let account = targetAccount.account
if let peerReference = PeerReference(targetPeer) {
let _ = fetchedMediaResource(
mediaBox: account.postbox.mediaBox,
userLocation: .peer(targetPeer.id),
userContentType: .avatar,
reference: .avatar(peer: peerReference, resource: resource)
).start()
}
self.accountSwitcherAvatarDisposable = (account.postbox.mediaBox.resourceData(resource)
|> filter { $0.complete }
|> take(1)
|> deliverOnMainQueue).start(next: { data in
if let uiImage = UIImage(contentsOfFile: data.path) {
buildButton(uiImage)
} else {
buildButton(nil)
}
})
} else {
self.accountSwitcherAvatarDisposable?.dispose()
buildButton(nil)
}
}
private func updateChatList(
hideStories: Bool,
networkState: AccountNetworkState,
proxy: (Bool, Bool),
passcode: (Bool, Bool),
@@ -7063,7 +7193,7 @@ private final class ChatListLocationContext {
switch location {
case let .chatList(groupId):
if groupId == .root {
defaultTitle = presentationData.strings.DialogList_Title
defaultTitle = ""
} else {
defaultTitle = presentationData.strings.ChatList_ArchivedChatsTitle
}
@@ -7080,6 +7210,7 @@ private final class ChatListLocationContext {
if case .chatList(.root) = self.location {
self.rightButton = nil
self.storyButton = nil
self.tabBarMoreButton = nil
self.accountSwitcherButton = nil
}
let title = !stateAndFilterId.state.selectedPeerIds.isEmpty ? presentationData.strings.ChatList_SelectedChats(Int32(stateAndFilterId.state.selectedPeerIds.count)) : defaultTitle
@@ -7096,6 +7227,7 @@ private final class ChatListLocationContext {
if case .chatList(.root) = self.location {
self.rightButton = nil
self.storyButton = nil
self.tabBarMoreButton = nil
self.accountSwitcherButton = nil
}
self.leftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
@@ -7104,6 +7236,7 @@ private final class ChatListLocationContext {
let _ = self?.parentController?.reorderingDonePressed()
}
)))
self.defaultLeftButton = self.leftButton
let (_, connectsViaProxy) = proxy
@@ -7118,7 +7251,13 @@ private final class ChatListLocationContext {
}*/
titleContent = NetworkStatusTitle(text: text, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
case .updating:
titleContent = NetworkStatusTitle(text: presentationData.strings.State_Updating, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
let updatingText: String
if case .chatList(.root) = self.location {
updatingText = ""
} else {
updatingText = presentationData.strings.State_Updating
}
titleContent = NetworkStatusTitle(text: updatingText, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
case .online:
titleContent = NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
}
@@ -7142,9 +7281,22 @@ private final class ChatListLocationContext {
}
)))
}
if SGSimpleSettings.shared.hideTabBar {
self.tabBarMoreButton = AnyComponentWithIdentity(id: "hiddenTabBarMore", component: AnyComponent(NavigationButtonComponent(
content: .more,
pressed: { [weak self] sourceView in
self?.performMoreAction(sourceView: sourceView)
},
contextAction: { [weak self] sourceView, _ in
self?.performMoreAction(sourceView: sourceView)
}
)))
} else {
self.tabBarMoreButton = nil
}
if isReorderingTabs {
self.leftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
self.defaultLeftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
content: .text(title: presentationData.strings.Common_Done, isBold: true),
pressed: { [weak self] _ in
let _ = self?.parentController?.reorderingDonePressed()
@@ -7152,14 +7304,14 @@ private final class ChatListLocationContext {
)))
} else {
if stateAndFilterId.state.editing {
self.leftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
self.defaultLeftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent(
content: .text(title: presentationData.strings.Common_Done, isBold: true),
pressed: { [weak self] _ in
self?.parentController?.donePressed()
}
)))
} else {
self.leftButton = AnyComponentWithIdentity(id: "edit", component: AnyComponent(NavigationButtonComponent(
self.defaultLeftButton = AnyComponentWithIdentity(id: "edit", component: AnyComponent(NavigationButtonComponent(
content: .text(title: presentationData.strings.Common_Edit, isBold: false),
pressed: { [weak self] _ in
self?.parentController?.editPressed()
@@ -7167,8 +7319,9 @@ private final class ChatListLocationContext {
)))
}
}
self.leftButton = self.defaultLeftButton
if storyPostingAvailable {
if storyPostingAvailable && !hideStories {
self.storyButton = AnyComponentWithIdentity(id: "story", component: AnyComponent(NavigationButtonComponent(
content: .icon(imageName: "Chat List/AddStoryIcon"),
pressed: { [weak self] _ in
@@ -7186,7 +7339,13 @@ private final class ChatListLocationContext {
} else {
self.storyButton = nil
}
if self.accountSwitcherButton == nil, let accountSwitcherInfo = self.accountSwitcherInfo {
self.updateAccountSwitcherButton(from: accountSwitcherInfo)
}
} else {
self.tabBarMoreButton = nil
let parentController = self.parentController
self.rightButton = AnyComponentWithIdentity(id: "more", component: AnyComponent(NavigationButtonComponent(
content: .more,
@@ -7202,6 +7361,7 @@ private final class ChatListLocationContext {
parentController.openArchiveMoreMenu(sourceView: sourceView, gesture: gesture)
}
)))
self.defaultLeftButton = self.leftButton
}
let (hasProxy, connectsViaProxy) = proxy
@@ -7220,7 +7380,8 @@ private final class ChatListLocationContext {
}
titleContent = NetworkStatusTitle(text: text, activity: true, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
case .updating:
titleContent = NetworkStatusTitle(text: presentationData.strings.State_Updating, activity: true, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
let updatingText: String = isRoot ? "" : presentationData.strings.State_Updating
titleContent = NetworkStatusTitle(text: updatingText, activity: true, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
case .online:
titleContent = NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
}
@@ -7397,8 +7558,13 @@ private final class ChatListLocationContext {
}
switch self.location {
case let .chatList(mode):
if case .archive = mode {
switch mode {
case .archive:
parentController.openArchiveMoreMenu(sourceView: sourceView, gesture: nil)
case .root:
if SGSimpleSettings.shared.hideTabBar {
parentController.settingsPressed()
}
}
case let .forum(peerId):
ChatListControllerImpl.openMoreMenu(context: self.context, peerId: peerId, sourceController: parentController, isViewingAsTopics: true, sourceView: sourceView, gesture: nil)
@@ -7425,3 +7591,15 @@ private final class AdsInfoContextReferenceContentSource: ContextReferenceConten
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds.inset(by: self.insets), insets: self.contentInsets)
}
}
// MARK: Swiftgram
extension ChatListControllerImpl {
@objc fileprivate func settingsPressed() {
if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface {
if let accountSettingsController = rootController.accountSettingsController {
(self.navigationController as? NavigationController)?.pushViewController(accountSettingsController)
}
}
}
}
@@ -1,3 +1,4 @@
import SGSimpleSettings
import Foundation
import UIKit
import AsyncDisplayKit
@@ -165,6 +166,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
previousItemNode.listNode.updatePeerGrouping = nil
previousItemNode.listNode.contentOffsetChanged = nil
previousItemNode.listNode.contentScrollingEnded = nil
previousItemNode.listNode.pinnedHeaderDisplayFractionUpdated = nil
previousItemNode.listNode.didBeginInteractiveDragging = nil
previousItemNode.listNode.endedInteractiveDragging = { _ in }
previousItemNode.listNode.shouldStopScrolling = nil
@@ -327,11 +329,12 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
}
return self.contentScrollingEnded?(listView) ?? false
//DispatchQueue.main.async { [weak self] in
// let _ = self?.contentScrollingEnded?(listView)
//}
//return false
}
itemNode.listNode.pinnedHeaderDisplayFractionUpdated = { [weak self] transition in
guard let self else {
return
}
self.pinnedHeaderDisplayFractionUpdated?(transition)
}
itemNode.listNode.activateChatPreview = { [weak self] item, threadId, sourceNode, gesture, location in
self?.activateChatPreview?(item, threadId, sourceNode, gesture, location)
@@ -427,6 +430,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
var contentOffset: ListViewVisibleContentOffset?
public var contentOffsetChanged: ((ListViewVisibleContentOffset, ListView) -> Void)?
public var contentScrollingEnded: ((ListView) -> Bool)?
public var pinnedHeaderDisplayFractionUpdated: ((ContainedViewLayoutTransition) -> Void)?
var didBeginInteractiveDragging: ((ListView) -> Void)?
var endedInteractiveDragging: ((ListView) -> Void)?
var shouldStopScrolling: ((ListView, CGFloat) -> Bool)?
@@ -443,6 +447,34 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
var canExpandHiddenItems: (() -> Bool)?
public var displayFilterLimit: (() -> Void)?
public var pinnedHeaderDisplayFraction: CGFloat {
guard let currentItemNodeValue = self.currentItemNodeValue else {
return 0.0
}
if self.transitionFraction != 0.0 {
var nextItemNode: ChatListContainerItemNode?
if let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
if self.transitionFraction < 0.0 {
if selectedIndex + 1 < self.availableFilters.count {
nextItemNode = self.itemNodes[self.availableFilters[selectedIndex + 1].id]
}
} else {
if selectedIndex > 0 {
nextItemNode = self.itemNodes[self.availableFilters[selectedIndex - 1].id]
}
}
}
if let nextItemNode {
let absTransitionFraction = abs(self.transitionFraction)
return (1.0 - absTransitionFraction) * currentItemNodeValue.listNode.pinnedScrollFraction + absTransitionFraction * nextItemNode.listNode.pinnedScrollFraction
} else {
return currentItemNodeValue.listNode.pinnedScrollFraction
}
}
return currentItemNodeValue.listNode.pinnedScrollFraction
}
public init(
context: AccountContext,
controller: ChatListControllerImpl?,
@@ -501,8 +533,8 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
self.applyItemNodeAsCurrent(id: .all, itemNode: itemNode)
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] _ in
guard let self, self.availableFilters.count > 1 || (self.controller?.isStoryPostingAvailable == true && !(self.context.sharedContext.callManager?.hasActiveCall ?? false)) else {
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] _ in // MARK: Swiftgram
guard let self, self.availableFilters.count > 1 || (self.controller?.isStoryPostingAvailable == true && !(self.context.sharedContext.callManager?.hasActiveCall ?? false) && !SGSimpleSettings.shared.disableSwipeToRecordStory) else {
return []
}
guard case .chatList(.root) = self.location else {
@@ -524,7 +556,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
} else {
return [.rightEdge]
}
}, edgeWidth: .widthMultiplier(factor: 1.0 / 6.0, min: 22.0, max: 80.0))
}, edgeWidth: SGSimpleSettings.shared.disableChatSwipeOptions ? .widthMultiplier(factor: 1.0 / 6.0, min: 0.0, max: 0.0) : .widthMultiplier(factor: 1.0 / 6.0, min: 22.0, max: 80.0))
panRecognizer.delegate = self.wrappedGestureRecognizerDelegate
panRecognizer.delaysTouchesBegan = false
panRecognizer.cancelsTouchesInView = true
@@ -553,8 +585,13 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
}
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
let filtersLimit = self.filtersLimit.flatMap({ $0 + 1 }) ?? Int32(self.availableFilters.count)
let maxFilterIndex = min(Int(filtersLimit), self.availableFilters.count) - 1
// MARK: Swiftgram
var _availableFilters = self.availableFilters
if SGSimpleSettings.shared.allChatsHidden {
_availableFilters.removeAll { $0 == .all }
}
let filtersLimit = self.filtersLimit.flatMap({ $0 + 1 }) ?? Int32(_availableFilters.count)
let maxFilterIndex = min(Int(filtersLimit), _availableFilters.count) - 1
switch recognizer.state {
case .began:
@@ -584,11 +621,12 @@ 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, .immediate, true)
self.pinnedHeaderDisplayFractionUpdated?(.immediate)
}
}
}
case .changed:
if let (layout, navigationBarHeight, visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation, inlineNavigationTransitionFraction, storiesInset) = self.validLayout, let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
if let (layout, navigationBarHeight, visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation, inlineNavigationTransitionFraction, storiesInset) = self.validLayout, let selectedIndex = _availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
let translation = recognizer.translation(in: self.view)
var transitionFraction = translation.x / layout.size.width
@@ -606,7 +644,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
hasLiveStream = true
}
if case .compact = layout.metrics.widthClass, self.controller?.isStoryPostingAvailable == true && !(self.context.sharedContext.callManager?.hasActiveCall ?? false) {
if case .compact = layout.metrics.widthClass, self.controller?.isStoryPostingAvailable == true && !(self.context.sharedContext.callManager?.hasActiveCall ?? false) && !SGSimpleSettings.shared.disableSwipeToRecordStory {
if hasLiveStream {
if translation.x >= 30.0 {
self.panRecognizer?.cancel()
@@ -661,9 +699,10 @@ 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)
self.pinnedHeaderDisplayFractionUpdated?(transition)
}
case .cancelled, .ended:
if let (layout, navigationBarHeight, visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation, inlineNavigationTransitionFraction, storiesInset) = self.validLayout, let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
if let (layout, navigationBarHeight, visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation, inlineNavigationTransitionFraction, storiesInset) = self.validLayout, let selectedIndex = _availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
let translation = recognizer.translation(in: self.view)
let velocity = recognizer.velocity(in: self.view)
var directionIsToRight: Bool?
@@ -700,7 +739,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
} else {
updatedIndex = max(updatedIndex - 1, 0)
}
let switchToId = self.availableFilters[updatedIndex].id
let switchToId = _availableFilters[updatedIndex].id
if switchToId != self.selectedId, let itemNode = self.itemNodes[switchToId] {
let _ = itemNode
self.selectedId = switchToId
@@ -723,6 +762,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
}
self.isSwitchingCurrentItemFilterByDragging = false
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false)
self.pinnedHeaderDisplayFractionUpdated?(transition)
}
default:
break
@@ -843,6 +883,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .spring)
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: transition)
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false)
self.pinnedHeaderDisplayFractionUpdated?(transition)
itemNode.emptyNode?.restartAnimation()
completion?()
} else if self.pendingItemNode == nil {
@@ -889,6 +930,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
strongSelf.selectedId = id
strongSelf.applyItemNodeAsCurrent(id: id, itemNode: itemNode)
strongSelf.currentItemFilterUpdated?(strongSelf.currentItemFilter, strongSelf.transitionFraction, .immediate, false)
strongSelf.pinnedHeaderDisplayFractionUpdated?(.immediate)
completion?()
return
@@ -945,6 +987,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
strongSelf.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)
strongSelf.currentItemFilterUpdated?(strongSelf.currentItemFilter, strongSelf.transitionFraction, transition, false)
strongSelf.pinnedHeaderDisplayFractionUpdated?(transition)
}
completion?()
@@ -1095,6 +1138,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
private var tapRecognizer: UITapGestureRecognizer?
var navigationBar: NavigationBar?
let navigationBarView = ComponentView<Empty>()
let sgFoldersView = ComponentView<Empty>()
weak var controller: ChatListControllerImpl?
var toolbar: Toolbar?
@@ -1171,12 +1215,16 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
self.addSubnode(self.mainContainerNode)
self.mainContainerNode.contentOffsetChanged = { [weak self] offset, listView in
self?.contentOffsetChanged(offset: offset, listView: listView, isPrimary: true)
}
self.mainContainerNode.contentScrollingEnded = { [weak self] listView in
return self?.contentScrollingEnded(listView: listView, isPrimary: true) ?? false
}
self.mainContainerNode.pinnedHeaderDisplayFractionUpdated = { [weak self] transition in
self?.pinnedHeaderDisplayFractionUpdated(transition: transition)
}
self.mainContainerNode.didBeginInteractiveDragging = { [weak self] listView in
self?.didBeginInteractiveDragging(listView: listView, isPrimary: true)
}
@@ -1361,7 +1409,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
}
}
private func updateNavigationBar(layout: ContainerViewLayout, deferScrollApplication: Bool, transition: ComponentTransition) -> (navigationHeight: CGFloat, storiesInset: CGFloat) {
private func updateNavigationBar(layout: ContainerViewLayout, deferScrollApplication: Bool, transition: ComponentTransition) -> (tabs: AnyComponent<Empty>?, navigationHeight: CGFloat, storiesInset: CGFloat) {
let headerContent = self.controller?.updateHeaderContent()
var panels: [HeaderPanelContainerComponent.Panel] = []
@@ -1379,6 +1427,8 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
return
}
switch notice {
case let .sgUrl(id, _, _, url, needAuth, permanent):
self.effectiveContainerNode.currentItemNode.interaction?.openSGAnnouncement(id, url, needAuth, permanent)
case .clearStorage:
self.effectiveContainerNode.currentItemNode.interaction?.openStorageManagement()
case .setupPassword:
@@ -1457,8 +1507,8 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
}
var navigationHeaderPanels: AnyComponent<Empty>?
var tabs: AnyComponent<Empty>? // MARK: Swiftgram
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 {
@@ -1481,7 +1531,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
switch entry {
case .all:
id = Int32.min
title = HorizontalTabsComponent.Tab.Title(text: self.presentationData.strings.ChatList_Tabs_All, entities: [], enableAnimations: false)
title = HorizontalTabsComponent.Tab.Title(text: sgUseShortAllChatsTitle(true) ? self.presentationData.strings.ChatList_Tabs_All : self.presentationData.strings.ChatList_Tabs_AllChats, entities: [], enableAnimations: false)
isMainTab = true
case let .filter(idValue, text, unread):
id = AnyHashable(idValue)
@@ -1581,7 +1631,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
navigationHeaderPanels = AnyComponent(HeaderPanelContainerComponent(
theme: self.presentationData.theme,
tabs: tabs,
tabs: (self.controller?.tabContainerData?.1 ?? false) ? nil : tabs, // MARK: Swiftgram
panels: panels
))
}
@@ -1663,9 +1713,9 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
}
transition.setFrame(view: navigationBarComponentView, frame: CGRect(origin: CGPoint(), size: navigationBarSize))
return (navigationBarSize.height, 0.0)
return (tabs, navigationBarSize.height, 0.0)
} else {
return (0.0, 0.0)
return (tabs, 0.0, 0.0)
}
}
@@ -1772,6 +1822,21 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
storiesInset = navigationBarLayout.storiesInset
self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, storiesInset)
// MARK: Swiftgram
let sgComponentTransition = ComponentTransition(transition)
let sgDisplayTabsAtBottom = self.controller?.tabContainerData?.1 ?? false
let sgShouldDisplayBottomFolders = sgDisplayTabsAtBottom && self.isSearchDisplayControllerActive == nil
let sgFoldersSize = self.sgFoldersView.update(
transition: sgComponentTransition,
component: AnyComponent(HeaderPanelContainerComponent(
theme: self.presentationData.theme,
tabs: navigationBarLayout.tabs,
panels: []
)),
environment: {},
containerSize: layout.size
)
var insets = layout.insets(options: [.input])
insets.top += navigationBarHeight
@@ -1826,6 +1891,11 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
toolbarNode?.removeFromSupernode()
})
}
// MARK: Swiftgram
if sgShouldDisplayBottomFolders && sgFoldersSize.height > 0.0 {
insets.bottom += sgFoldersSize.height + 16.0 + 8.0
}
var childrenLayout = layout
childrenLayout.intrinsicInsets = UIEdgeInsets(top: visualNavigationHeight, left: childrenLayout.intrinsicInsets.left, bottom: childrenLayout.intrinsicInsets.bottom, right: childrenLayout.intrinsicInsets.right)
@@ -1890,6 +1960,41 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
navigationBarComponentView.deferScrollApplication = false
navigationBarComponentView.applyCurrentScroll(transition: ComponentTransition(transition))
}
// MARK: Swiftgram
if let sgFoldersView = self.sgFoldersView.view as? HeaderPanelContainerComponent.View {
if sgShouldDisplayBottomFolders && sgFoldersSize.height > 0.0 {
if sgFoldersView.superview == nil {
self.view.addSubview(sgFoldersView)
if transition.isAnimated {
sgFoldersView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.23)
} else {
sgFoldersView.alpha = 1.0
}
}
// Magic
// var heightInset: CGFloat = 0.0
// if case .forum = self.location {
// heightInset = 4.0
// }
// var tabBarHeight: CGFloat
let bottomInset: CGFloat = layout.insets(options: []).bottom
// if !layout.safeInsets.left.isZero {
// tabBarHeight = 34.0 + bottomInset
// } else {
// tabBarHeight = 49.0 - heightInset + bottomInset
// }
//
// TODO(swiftgram):
sgComponentTransition.setFrame(view: sgFoldersView, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomInset - sgFoldersSize.height - 16.0), size: sgFoldersSize))
} else {
sgFoldersView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak sgFoldersView] _ in
sgFoldersView?.removeFromSuperview()
})
}
}
//
}
@MainActor
@@ -2172,6 +2277,19 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
return false
}
private func pinnedHeaderDisplayFractionUpdated(transition: ContainedViewLayoutTransition) {
var pinnedFraction: CGFloat = 0.0
if let inlineStackContainerNode = self.inlineStackContainerNode, self.inlineStackContainerTransitionFraction != 0.0 {
pinnedFraction = (1.0 - self.inlineStackContainerTransitionFraction) * self.mainContainerNode.pinnedHeaderDisplayFraction + self.inlineStackContainerTransitionFraction * inlineStackContainerNode.pinnedHeaderDisplayFraction
} else {
pinnedFraction = self.mainContainerNode.pinnedHeaderDisplayFraction
}
if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
navigationBarComponentView.updateEdgeEffectForPinnedFraction(pinnedFraction: pinnedFraction, transition: ComponentTransition(transition))
}
}
func makeInlineChatList(location: ChatListControllerLocation) -> ChatListContainerNode {
var forumPeerId: EnginePeer.Id?
if case let .forum(peerId) = location {
@@ -2220,6 +2338,9 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
inlineStackContainerNode.contentScrollingEnded = { [weak self] listView in
return self?.contentScrollingEnded(listView: listView, isPrimary: false) ?? false
}
inlineStackContainerNode.pinnedHeaderDisplayFractionUpdated = { [weak self] transition in
self?.pinnedHeaderDisplayFractionUpdated(transition: transition)
}
inlineStackContainerNode.activateChatPreview = self.mainContainerNode.activateChatPreview
inlineStackContainerNode.openStories = self.mainContainerNode.openStories
@@ -1268,6 +1268,7 @@ private final class ChatListFilterPresetController: ItemListController {
let presentationInterfaceState = ChatPresentationInterfaceState(
chatWallpaper: .builtin(WallpaperSettings()),
theme: presentationData.theme,
preferredGlassType: .default,
strings: presentationData.strings,
dateTimeFormat: presentationData.dateTimeFormat,
nameDisplayOrder: presentationData.nameDisplayOrder,
@@ -1,4 +1,5 @@
import Foundation
import SGSimpleSettings
import UIKit
import AsyncDisplayKit
import Display
@@ -806,6 +807,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
filtersInsets.bottom += 48.0
}
// TODO(swiftgram): too high if used from top bar
if !SGSimpleSettings.shared.showTabNames { filtersInsets.bottom += 16.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)
@@ -1174,7 +1178,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
return items
}
let controller = ContextController(presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node, shouldBeDismissed: shouldBeDismissed)), items: items |> map { ContextController.Items(content: .list($0)) }, recognizer: nil, gesture: gesture)
let controller = makeContextController(presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node, shouldBeDismissed: shouldBeDismissed)), items: items |> map { ContextController.Items(content: .list($0)) }, recognizer: nil, gesture: gesture)
self.presentInGlobalOverlay?(controller, nil)
return
@@ -1248,7 +1252,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
return items
}
let controller = ContextController(presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: items |> map { ContextController.Items(content: .list($0)) }, recognizer: nil, gesture: gesture)
let controller = makeContextController(presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: items |> map { ContextController.Items(content: .list($0)) }, recognizer: nil, gesture: gesture)
self.presentInGlobalOverlay?(controller, nil)
}
@@ -1312,7 +1316,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
switch previewData {
case let .gallery(gallery):
gallery.setHintWillBePresentedInPreviewingContext(true)
let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay?(contextController, nil)
case .instantPage:
break
@@ -1,3 +1,4 @@
import SGSimpleSettings
import Foundation
import UIKit
import AsyncDisplayKit
@@ -704,7 +705,7 @@ private func revealOptions(strings: PresentationStrings, theme: PresentationThem
}
}
}
if canDelete {
if canDelete && !SGSimpleSettings.shared.disableDeleteChatSwipeOption {
options.append(ItemListRevealOption(key: RevealOptionKey.delete.rawValue, title: strings.Common_Delete, icon: deleteIcon, color: theme.list.itemDisclosureActions.destructive.fillColor, textColor: theme.list.itemDisclosureActions.destructive.foregroundColor))
}
if case .savedMessagesChats = location {
@@ -792,7 +793,7 @@ private func forumThreadRevealOptions(strings: PresentationStrings, theme: Prese
}
}
}
if canDelete {
if canDelete && !SGSimpleSettings.shared.disableDeleteChatSwipeOption {
options.append(ItemListRevealOption(key: RevealOptionKey.delete.rawValue, title: strings.Common_Delete, icon: deleteIcon, color: theme.list.itemDisclosureActions.destructive.fillColor, textColor: theme.list.itemDisclosureActions.destructive.foregroundColor))
}
if canOpenClose {
@@ -1756,7 +1757,10 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if case let .groupReference(groupReference) = item.content {
storyState = groupReference.storyState
}
// MARK: Swiftgram
let sgCompactChatList = SGSimpleSettings.shared.compactChatList
let sgCompactMessagePreview = SGCompactMessagePreviewLayout.isEnabled()
let sgAvatarScaleDivisor: CGFloat = SGCompactMessagePreviewLayout.avatarScaleDivisor(compactChatList: sgCompactChatList, compactMessagePreview: sgCompactMessagePreview)
var peer: EnginePeer?
var displayAsMessage = false
var enablePreview = true
@@ -1917,8 +1921,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
isForumAvatar = true
}
}
var avatarDiameter = min(60.0, floor(item.presentationData.fontSize.baseDisplaySize * 60.0 / 17.0))
// MARK: Swiftgram
var avatarDiameter = min(60.0, floor(item.presentationData.fontSize.baseDisplaySize * 60.0 / 17.0)) / sgAvatarScaleDivisor
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData, customMessageListData.commandPrefix != nil {
avatarDiameter = 40.0
@@ -2169,6 +2173,12 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
let currentChatListQuoteSearchResult = self.cachedChatListQuoteSearchResult
let currentCustomTextEntities = self.cachedCustomTextEntities
// MARK: Swiftgram
let sgCompactChatList = SGSimpleSettings.shared.compactChatList
let sgCompactMessagePreview = SGCompactMessagePreviewLayout.isEnabled()
let sgAvatarScaleDivisor: CGFloat = SGCompactMessagePreviewLayout.avatarScaleDivisor(compactChatList: sgCompactChatList, compactMessagePreview: sgCompactMessagePreview)
return { item, params, first, last, firstWithHeader, nextIsPinned in
let titleFont = Font.medium(floor(item.presentationData.fontSize.itemListBaseFontSize * 16.0 / 17.0))
let textFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 15.0 / 17.0))
@@ -2427,9 +2437,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
let enableChatListPhotos = true
// MARK: Swiftgram
// if changed, adjust setupItem accordingly
var avatarDiameter = min(60.0, floor(item.presentationData.fontSize.baseDisplaySize * 60.0 / 17.0))
var avatarDiameter = min(60.0, floor(item.presentationData.fontSize.baseDisplaySize * 60.0 / 17.0)) / sgAvatarScaleDivisor
let avatarLeftInset: CGFloat
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData, customMessageListData.commandPrefix != nil {
@@ -2497,7 +2507,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
contentData = .group(peers: groupPeers)
hideAuthor = true
}
// MARK: Swiftgram
let sgInlineAuthorPrefix = sgCompactMessagePreview && !hideAuthor
if sgCompactChatList || sgCompactMessagePreview { hideAuthor = true };
var attributedText: NSAttributedString
var hasDraft = false
@@ -2525,7 +2537,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
}
}
// MARK: Swiftgram
if sgCompactChatList || sgInlineAuthorPrefix { useInlineAuthorPrefix = true };
if useInlineAuthorPrefix {
if case let .user(author) = messages.last?.author {
if author.id == item.context.account.peerId {
@@ -3590,12 +3603,22 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
textMaxWidth -= 18.0
}
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: textAttributedString, backgroundColor: nil, maximumNumberOfLines: (authorAttributedString == nil && itemTags.isEmpty && forumThread == nil && topForumTopicItems.isEmpty) ? 2 : 1, truncationType: .end, constrainedSize: CGSize(width: textMaxWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: textCutout, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0)))
// MARK: Swiftgram
let sgChatListMaxLines: Int
if sgCompactMessagePreview {
sgChatListMaxLines = 1
} else {
sgChatListMaxLines = (authorAttributedString == nil && itemTags.isEmpty && forumThread == nil && topForumTopicItems.isEmpty && !sgCompactChatList) ? 2 : 1
}
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: textAttributedString, backgroundColor: nil, maximumNumberOfLines: sgChatListMaxLines, truncationType: .end, constrainedSize: CGSize(width: textMaxWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: textCutout, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0)))
let maxTitleLines: Int
switch item.index {
case .forum:
// MARK: Swiftgram
if sgCompactChatList { maxTitleLines = 1 } else {
maxTitleLines = 2
}
case .chatList:
maxTitleLines = 1
}
@@ -3722,6 +3745,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
peerRevealOptions = [
ItemListRevealOption(key: RevealOptionKey.hidePsa.rawValue, title: item.presentationData.strings.ChatList_HideAction, icon: deleteIcon, color: item.presentationData.theme.list.itemDisclosureActions.inactive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.neutral1.foregroundColor)
]
// MARK: Swiftgram
if SGSimpleSettings.shared.disableDeleteChatSwipeOption { peerRevealOptions.removeLast() }
peerLeftRevealOptions = []
} else if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData {
peerLeftRevealOptions = []
@@ -3730,6 +3755,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
ItemListRevealOption(key: RevealOptionKey.edit.rawValue, title: item.presentationData.strings.ChatList_ItemMenuEdit, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.neutral2.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.neutral2.foregroundColor),
ItemListRevealOption(key: RevealOptionKey.delete.rawValue, title: item.presentationData.strings.ChatList_ItemMenuDelete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)
]
// MARK: Swiftgram
if SGSimpleSettings.shared.disableDeleteChatSwipeOption { peerRevealOptions.removeLast() }
} else {
peerRevealOptions = []
}
@@ -3772,6 +3799,16 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
peerRevealOptions = []
peerLeftRevealOptions = []
}
// MARK: Swiftgram
if sgCompactChatList {
peerRevealOptions = peerRevealOptions.map { option in
ItemListRevealOption(key: option.key, title: option.title, icon: .none, color: option.color, textColor: option.textColor)
}
peerLeftRevealOptions = peerLeftRevealOptions.map { option in
ItemListRevealOption(key: option.key, title: option.title, icon: .none, color: option.color, textColor: option.textColor)
}
}
//
let (onlineLayout, onlineApply) = onlineLayout(online, onlineIsVoiceChat)
var animateContent = false
@@ -3794,7 +3831,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
itemHeight += titleSpacing
itemHeight += authorSpacing
}
// MARK: Swiftgram
itemHeight = itemHeight / (sgCompactChatList ? 1.5 : 1.0)
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: layoutOffset + floor(item.presentationData.fontSize.itemListBaseFontSize * 8.0 / 17.0)), size: CGSize(width: rawContentWidth, height: itemHeight - 12.0 - 9.0))
let insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: firstWithHeader)
@@ -3952,7 +3990,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
var avatarScaleOffset: CGFloat = 0.0
var avatarScale: CGFloat = 1.0
if let inlineNavigationLocation = item.interaction.inlineNavigationLocation {
let targetAvatarScale: CGFloat = floor(item.presentationData.fontSize.itemListBaseFontSize * 54.0 / 17.0) / avatarFrame.width
// MARK: Swiftgram
let targetAvatarScale: CGFloat = floor(item.presentationData.fontSize.itemListBaseFontSize * 54.0 / 17.0) / sgAvatarScaleDivisor / avatarFrame.width
avatarScale = targetAvatarScale * inlineNavigationLocation.progress + 1.0 * (1.0 - inlineNavigationLocation.progress)
let targetAvatarScaleOffset: CGFloat = -(avatarFrame.width - avatarFrame.width * avatarScale) * 0.5
@@ -4305,16 +4344,19 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.statusNode.fontSize = item.presentationData.fontSize.itemListBaseFontSize
let _ = strongSelf.statusNode.transitionToState(statusState, animated: animateContent)
// MARK: Swiftgram
let sizeFactor = item.presentationData.fontSize.itemListBaseFontSize / 17.0
let sgCompactMessagePreviewBadgeOffset = SGCompactMessagePreviewLayout.badgeOffset(sizeFactor: sizeFactor, compactMessagePreview: sgCompactMessagePreview, compactChatList: sgCompactChatList)
var nextBadgeX: CGFloat = contentRect.maxX
if let _ = currentBadgeBackgroundImage {
let badgeFrame = CGRect(x: nextBadgeX - badgeLayout.width, y: contentRect.maxY - badgeLayout.height - 2.0, width: badgeLayout.width, height: badgeLayout.height)
let badgeFrame = CGRect(x: nextBadgeX - badgeLayout.width, y: contentRect.maxY - badgeLayout.height - 2.0 + (sgCompactChatList ? 13.0 / sizeFactor : 0.0) - sgCompactMessagePreviewBadgeOffset, width: badgeLayout.width, height: badgeLayout.height)
transition.updateFrame(node: strongSelf.badgeNode, frame: badgeFrame)
nextBadgeX -= badgeLayout.width + 6.0
}
if currentMentionBadgeImage != nil || currentBadgeBackgroundImage != nil {
let badgeFrame = CGRect(x: nextBadgeX - mentionBadgeLayout.width, y: contentRect.maxY - mentionBadgeLayout.height - 2.0, width: mentionBadgeLayout.width, height: mentionBadgeLayout.height)
let badgeFrame = CGRect(x: nextBadgeX - mentionBadgeLayout.width, y: contentRect.maxY - mentionBadgeLayout.height - 2.0 + (sgCompactChatList ? 13.0 / sizeFactor : 0.0) - sgCompactMessagePreviewBadgeOffset, width: mentionBadgeLayout.width, height: mentionBadgeLayout.height)
transition.updateFrame(node: strongSelf.mentionBadgeNode, frame: badgeFrame)
nextBadgeX -= mentionBadgeLayout.width + 6.0
@@ -4325,7 +4367,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.pinnedIconNode.isHidden = false
let pinnedIconSize = currentPinnedIconImage.size
let pinnedIconFrame = CGRect(x: nextBadgeX - pinnedIconSize.width, y: contentRect.maxY - pinnedIconSize.height - 2.0, width: pinnedIconSize.width, height: pinnedIconSize.height)
let pinnedIconFrame = CGRect(x: nextBadgeX - pinnedIconSize.width, y: contentRect.maxY - pinnedIconSize.height - 2.0 + (sgCompactChatList ? 13.0 / sizeFactor : 0.0) - sgCompactMessagePreviewBadgeOffset, width: pinnedIconSize.width, height: pinnedIconSize.height)
strongSelf.pinnedIconNode.frame = pinnedIconFrame
nextBadgeX -= pinnedIconSize.width + 6.0
@@ -4339,7 +4381,17 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
let actionButtonTopInset = floor(item.presentationData.fontSize.itemListBaseFontSize * 5.0 / 17.0)
let actionButtonBottomInset = floor(item.presentationData.fontSize.itemListBaseFontSize * 4.0 / 17.0)
let actionButtonSize = CGSize(width: actionButtonTitleNodeLayout.size.width + actionButtonSideInset * 2.0, height: actionButtonTitleNodeLayout.size.height + actionButtonTopInset + actionButtonBottomInset)
var actionButtonSize = CGSize(width: actionButtonTitleNodeLayout.size.width + actionButtonSideInset * 2.0, height: actionButtonTitleNodeLayout.size.height + actionButtonTopInset + actionButtonBottomInset)
// MARK: Swiftgram
let sgActionButtonScaleDivisor: CGFloat = sgCompactChatList ? 1.5 : 1.0
if sgCompactChatList {
let sgCompactActionButtonSideInset = floor(item.presentationData.fontSize.itemListBaseFontSize * 3.0 / 17.0)
actionButtonSize.width = max(actionButtonSize.width / sgActionButtonScaleDivisor, actionButtonTitleNodeLayout.size.width + sgCompactActionButtonSideInset * 2.0)
} else {
actionButtonSize.width = actionButtonSize.width / sgActionButtonScaleDivisor
}
actionButtonSize.height = actionButtonSize.height / sgActionButtonScaleDivisor
//
var actionButtonFrame = CGRect(x: nextBadgeX - actionButtonSize.width, y: contentRect.minY + floor((contentRect.height - actionButtonSize.height) * 0.5), width: actionButtonSize.width, height: actionButtonSize.height)
actionButtonFrame.origin.y = max(actionButtonFrame.origin.y, dateFrame.maxY + floor(item.presentationData.fontSize.itemListBaseFontSize * 4.0 / 17.0))
@@ -4377,7 +4429,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
actionButtonNode.frame = actionButtonFrame
actionButtonBackgroundView.frame = CGRect(origin: CGPoint(), size: actionButtonFrame.size)
actionButtonTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((actionButtonFrame.width - actionButtonTitleNodeLayout.size.width) * 0.5), y: actionButtonTopInset), size: actionButtonTitleNodeLayout.size)
let sgActionButtonTitleYOffset: CGFloat = sgCompactChatList ? floorToScreenPixels(1.0 * sizeFactor) : 0.0
actionButtonTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(max(0.0, (actionButtonFrame.width - actionButtonTitleNodeLayout.size.width) * 0.5)), y: floorToScreenPixels((actionButtonFrame.height - actionButtonTitleNodeLayout.size.height) * 0.5 + sgActionButtonTitleYOffset)), size: actionButtonTitleNodeLayout.size)
nextBadgeX -= actionButtonSize.width + 6.0
} else {
@@ -4395,6 +4448,10 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
}
// MARK: Swiftgram
let sgCompactMessagePreviewVerticalOffset: CGFloat = SGCompactMessagePreviewLayout.textVerticalOffset(sizeFactor: sizeFactor, compactMessagePreview: sgCompactMessagePreview, compactChatList: sgCompactChatList, hasAuthorLine: !authorLayout.height.isZero)
let sgCompactMessagePreviewTitleTextSpacing: CGFloat = SGCompactMessagePreviewLayout.titleTextSpacing(sizeFactor: sizeFactor, compactMessagePreview: sgCompactMessagePreview, compactChatList: sgCompactChatList, hasAuthorLine: !authorLayout.height.isZero)
//
var titleOffset: CGFloat = titleLeftOffset
if let currentSecretIconImage = currentSecretIconImage {
let iconNode: ASImageNode
@@ -4409,7 +4466,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.secretIconNode = iconNode
}
iconNode.image = currentSecretIconImage
transition.updateFrame(node: iconNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + titleLeftOffset, y: contentRect.origin.y + floor((titleLayout.size.height - currentSecretIconImage.size.height) / 2.0)), size: currentSecretIconImage.size))
transition.updateFrame(node: iconNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + titleLeftOffset, y: contentRect.origin.y + floor((titleLayout.size.height - currentSecretIconImage.size.height) / 2.0) + sgCompactMessagePreviewVerticalOffset), size: currentSecretIconImage.size))
titleOffset += currentSecretIconImage.size.width + 3.0
} else if let secretIconNode = strongSelf.secretIconNode {
strongSelf.secretIconNode = nil
@@ -4417,12 +4474,12 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
let contentDelta = CGPoint(x: contentRect.origin.x - (strongSelf.titleNode.frame.minX - titleOffset), y: contentRect.origin.y - (strongSelf.titleNode.frame.minY - UIScreenPixel))
let titleFrame = CGRect(origin: CGPoint(x: contentRect.origin.x + titleOffset, y: contentRect.origin.y + UIScreenPixel), size: titleLayout.size)
let titleFrame = CGRect(origin: CGPoint(x: contentRect.origin.x + titleOffset, y: contentRect.origin.y + UIScreenPixel + sgCompactMessagePreviewVerticalOffset), size: titleLayout.size)
strongSelf.titleNode.frame = titleFrame
let authorNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height), size: authorLayout)
let authorNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height + sgCompactMessagePreviewVerticalOffset), size: authorLayout)
strongSelf.authorNode.frame = authorNodeFrame
let textNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height - 1.0 + UIScreenPixel + (authorLayout.height.isZero ? 0.0 : (authorLayout.height - 3.0))), size: textLayout.size)
let textNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height - 1.0 + UIScreenPixel + (authorLayout.height.isZero ? 0.0 : (authorLayout.height - 3.0)) + sgCompactMessagePreviewVerticalOffset + sgCompactMessagePreviewTitleTextSpacing), size: textLayout.size)
if let topForumTopicRect, !isSearching {
let compoundHighlightingNode: LinkHighlightingNode
@@ -4604,8 +4661,11 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
if !itemTags.isEmpty {
let sizeFactor = item.presentationData.fontSize.itemListBaseFontSize / 17.0
// MARK: Swiftgram
let sgCompactMessagePreviewTagListOffset = max(0.0, SGCompactMessagePreviewLayout.textBlockOffset(sizeFactor: sizeFactor, compactMessagePreview: sgCompactMessagePreview, compactChatList: sgCompactChatList, hasAuthorLine: false) - floorToScreenPixels(4.0 * sizeFactor))
//
let itemTagListFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: contentRect.minY + measureLayout.size.height * 2.0 + floorToScreenPixels(2.0 * sizeFactor)), size: CGSize(width: contentRect.width, height: floorToScreenPixels(20.0 * sizeFactor)))
let itemTagListFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: contentRect.minY + measureLayout.size.height * 2.0 + floorToScreenPixels(2.0 * sizeFactor) + sgCompactMessagePreviewTagListOffset), size: CGSize(width: contentRect.width, height: floorToScreenPixels(20.0 * sizeFactor)))
var itemTagListTransition = transition
let itemTagList: ComponentView<Empty>
@@ -5092,7 +5152,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.updateLayout(size: CGSize(width: layout.contentSize.width, height: itemHeight), leftInset: params.leftInset, rightInset: params.rightInset)
if item.editing {
if item.editing || SGSimpleSettings.shared.disableChatSwipeOptions {
strongSelf.setRevealOptions((left: [], right: []), enableAnimations: item.context.sharedContext.energyUsageSettings.fullTranslucency)
} else {
strongSelf.setRevealOptions((left: peerLeftRevealOptions, right: peerRevealOptions), enableAnimations: item.context.sharedContext.energyUsageSettings.fullTranslucency)
@@ -190,6 +190,10 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
var alignment: NSTextAlignment = .left
switch item.notice {
case let .sgUrl(_, title, text, _, _, _):
let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: title, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
titleString = titleStringValue
textString = NSAttributedString(string: text ?? "", font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
case let .clearStorage(sizeFraction):
let sizeString = dataSizeString(Int64(sizeFraction), formatting: DataSizeStringFormatting(strings: item.strings, decimalSeparator: "."))
let rawTitleString = item.strings.ChatList_StorageHintTitle(sizeString)