mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-07-10 18:07:04 +02:00
Update Ghostgram features
This commit is contained in:
@@ -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 (subscribe→fetch)
|
||||
// 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)
|
||||
|
||||
Reference in New Issue
Block a user