From a4e2c4f398d59a1d76e5efe28c7b2e3be3f7f21e Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Tue, 12 Dec 2023 14:06:19 +0400 Subject: [PATCH] [WIP] Saved messages --- .../PeerInfo/PeerInfoChatListPaneNode/BUILD | 29 +++ .../Sources/PeerInfoChatListPaneNode.swift | 205 ++++++++++++++++++ .../PeerInfo/PeerInfoPaneNode/BUILD | 23 ++ .../Sources/PeerInfoPaneNode.swift | 57 +++++ .../SavedMessages/SavedMessagesScreen/BUILD | 26 +++ .../Sources/SavedMessagesScreen.swift | 86 ++++++++ 6 files changed, 426 insertions(+) create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/BUILD create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/BUILD create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift create mode 100644 submodules/TelegramUI/Components/SavedMessages/SavedMessagesScreen/BUILD create mode 100644 submodules/TelegramUI/Components/SavedMessages/SavedMessagesScreen/Sources/SavedMessagesScreen.swift diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/BUILD new file mode 100644 index 0000000000..cfd1866200 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/BUILD @@ -0,0 +1,29 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PeerInfoChatListPaneNode", + module_name = "PeerInfoChatListPaneNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/TelegramStringFormatting", + "//submodules/ShimmerEffect", + "//submodules/ComponentFlow", + "//submodules/AppBundle", + "//submodules/ChatListUI", + "//submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift new file mode 100644 index 0000000000..e04ef85c6b --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift @@ -0,0 +1,205 @@ +import AsyncDisplayKit +import Display +import TelegramCore +import SwiftSignalKit +import Postbox +import TelegramPresentationData +import AccountContext +import ContextUI +import TelegramStringFormatting +import ShimmerEffect +import ComponentFlow +import TelegramNotices +import TelegramUIPreferences +import AppBundle +import PeerInfoPaneNode +import ChatListUI + +public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate, UIGestureRecognizerDelegate { + private let context: AccountContext + + private let navigationController: () -> NavigationController? + + public weak var parentController: ViewController? + + private var currentParams: (size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData)? + + private let ready = Promise() + private var didSetReady: Bool = false + public var isReady: Signal { + return self.ready.get() + } + + private let statusPromise = Promise(nil) + public var status: Signal { + self.statusPromise.get() + } + + public var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)? + public var tabBarOffset: CGFloat { + return 0.0 + } + + private var presentationData: PresentationData + private var presentationDataDisposable: Disposable? + + private let chatListNode: ChatListNode + + public init(context: AccountContext, navigationController: @escaping () -> NavigationController?) { + self.context = context + self.navigationController = navigationController + self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + + self.chatListNode = ChatListNode( + context: self.context, + location: .savedMessagesChats, + chatListFilter: nil, + previewing: false, + fillPreloadItems: false, + mode: .chatList(appendContacts: false), + isPeerEnabled: nil, + theme: self.presentationData.theme, + fontSize: self.presentationData.listsFontSize, + strings: self.presentationData.strings, + dateTimeFormat: self.presentationData.dateTimeFormat, + nameSortOrder: self.presentationData.nameSortOrder, + nameDisplayOrder: self.presentationData.nameDisplayOrder, + animationCache: self.context.animationCache, + animationRenderer: self.context.animationRenderer, + disableAnimations: false, + isInlineMode: false, + autoSetReady: false, + isMainTab: nil + ) + + super.init() + + self.addSubnode(self.chatListNode) + + self.presentationDataDisposable = (self.context.sharedContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + guard let self else { + return + } + self.presentationData = presentationData + }) + + self.ready.set(self.chatListNode.ready) + + self.chatListNode.peerSelected = { [weak self] peer, _, _, _, _ in + guard let self, let navigationController = self.navigationController() else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + context: self.context, + chatLocation: .replyThread(ChatReplyThreadMessage( + messageId: makeThreadIdMessageId( + peerId: self.context.account.peerId, + threadId: peer.id.toInt64() + ), + channelMessageId: nil, + isChannelPost: false, + isForumPost: false, + maxMessage: nil, + maxReadIncomingMessageId: nil, + maxReadOutgoingMessageId: nil, + unreadCount: 0, + initialFilledHoles: IndexSet(), + initialAnchor: .automatic, + isNotAvailable: false + )), + subject: nil, + keepStack: .always + )) + self.chatListNode.clearHighlightAnimated(true) + } + } + + deinit { + self.presentationDataDisposable?.dispose() + } + + public func ensureMessageIsVisible(id: MessageId) { + } + + public func scrollToTop() -> Bool { + return false + } + + public func hitTestResultForScrolling() -> UIView? { + return nil + } + + public func brieflyDisableTouchActions() { + } + + public func findLoadedMessage(id: MessageId) -> Message? { + return nil + } + + public func updateHiddenMedia() { + } + + public func transferVelocity(_ velocity: CGFloat) { + } + + public func cancelPreviewGestures() { + } + + public func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? { + return nil + } + + public func addToTransitionSurface(view: UIView) { + } + + override public func didLoad() { + super.didLoad() + } + + + override public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } + + public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if gestureRecognizer.state != .failed, let otherGestureRecognizer = otherGestureRecognizer as? UIPanGestureRecognizer { + let _ = otherGestureRecognizer + return true + } else { + return false + } + } + + public func updateSelectedMessages(animated: Bool) { + } + + public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) + + transition.updateFrame(node: self.chatListNode, frame: CGRect(origin: CGPoint(), size: size)) + let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) + self.chatListNode.updateLayout( + transition: transition, + updateSizeAndInsets: ListViewUpdateSizeAndInsets( + size: size, + insets: UIEdgeInsets(top: topInset, left: sideInset, bottom: bottomInset, right: sideInset), + duration: duration, + curve: curve + ), + visibleTopInset: topInset, + originalTopInset: topInset, + storiesInset: 0.0, + inlineNavigationLocation: nil, + inlineNavigationTransitionFraction: 0.0 + ) + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let result = super.hitTest(point, with: event) else { + return nil + } + return result + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/BUILD new file mode 100644 index 0000000000..1e8570712a --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/BUILD @@ -0,0 +1,23 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PeerInfoPaneNode", + module_name = "PeerInfoPaneNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/TelegramPresentationData", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift new file mode 100644 index 0000000000..406286f904 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift @@ -0,0 +1,57 @@ +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import Display +import TelegramPresentationData + +public enum PeerInfoPaneKey: Int32 { + case members + case stories + case media + case files + case music + case voice + case links + case gifs + case groupsInCommon + case recommended + case savedMessagesChats +} + +public struct PeerInfoStatusData: Equatable { + public var text: String + public var isActivity: Bool + public var key: PeerInfoPaneKey? + + public init( + text: String, + isActivity: Bool, + key: PeerInfoPaneKey? + ) { + self.text = text + self.isActivity = isActivity + self.key = key + } +} + +public protocol PeerInfoPaneNode: ASDisplayNode { + var isReady: Signal { get } + + var parentController: ViewController? { get set } + + var status: Signal { get } + var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)? { get set } + var tabBarOffset: CGFloat { get } + + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) + func scrollToTop() -> Bool + func transferVelocity(_ velocity: CGFloat) + func cancelPreviewGestures() + func findLoadedMessage(id: MessageId) -> Message? + func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? + func addToTransitionSurface(view: UIView) + func updateHiddenMedia() + func updateSelectedMessages(animated: Bool) + func ensureMessageIsVisible(id: MessageId) +} diff --git a/submodules/TelegramUI/Components/SavedMessages/SavedMessagesScreen/BUILD b/submodules/TelegramUI/Components/SavedMessages/SavedMessagesScreen/BUILD new file mode 100644 index 0000000000..8606deae33 --- /dev/null +++ b/submodules/TelegramUI/Components/SavedMessages/SavedMessagesScreen/BUILD @@ -0,0 +1,26 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "SavedMessagesScreen", + module_name = "SavedMessagesScreen", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Display", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/ComponentFlow", + "//submodules/Components/ViewControllerComponent", + "//submodules/Components/ComponentDisplayAdapters", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/SavedMessages/SavedMessagesScreen/Sources/SavedMessagesScreen.swift b/submodules/TelegramUI/Components/SavedMessages/SavedMessagesScreen/Sources/SavedMessagesScreen.swift new file mode 100644 index 0000000000..9fd1b3bdab --- /dev/null +++ b/submodules/TelegramUI/Components/SavedMessages/SavedMessagesScreen/Sources/SavedMessagesScreen.swift @@ -0,0 +1,86 @@ +import Foundation +import AsyncDisplayKit +import Display +import ComponentFlow +import ComponentDisplayAdapters +import TelegramCore +import AccountContext +import SwiftSignalKit +import ViewControllerComponent + +private final class SavedMessagesScreenComponent: Component { + public let context: AccountContext + + public init( + context: AccountContext + ) { + self.context = context + } + + public static func ==(lhs: SavedMessagesScreenComponent, rhs: SavedMessagesScreenComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + return true + } + + public final class View: UIView { + private var component: SavedMessagesScreenComponent? + private var state: EmptyComponentState? + private var environment: ViewControllerComponentContainer.Environment? + + override public init(frame: CGRect) { + super.init(frame: frame) + + self.clipsToBounds = true + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: SavedMessagesScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let environment = environment[ViewControllerComponentContainer.Environment.self].value + + let themeUpdated = self.environment?.theme !== environment.theme + + self.environment = environment + self.component = component + self.state = state + + if themeUpdated { + self.backgroundColor = environment.theme.list.plainBackgroundColor + } + + return availableSize + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +public class SavedMessagesScreen: ViewControllerComponentContainer { + private let context: AccountContext + + public init(context: AccountContext) { + self.context = context + + super.init(context: context, component: SavedMessagesScreenComponent(context: context), navigationBarAppearance: .none) + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } +}