mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Folder improvements
This commit is contained in:
parent
05518d735d
commit
3825ddd778
@ -92,6 +92,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen",
|
"//submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen",
|
||||||
"//submodules/ItemListUI",
|
"//submodules/ItemListUI",
|
||||||
"//submodules/QrCodeUI",
|
"//submodules/QrCodeUI",
|
||||||
|
"//submodules/TelegramUI/Components/ActionPanelComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -2769,27 +2769,29 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let previewScreen = ChatFolderLinkPreviewScreen(
|
if links == nil || links?.count == 0 {
|
||||||
context: self.context,
|
openCreateChatListFolderLink(context: self.context, folderId: filterId, checkIfExists: false, title: title, peerIds: data.includePeers.peers, pushController: { [weak self] c in
|
||||||
subject: .linkList(folderId: filterId, initialLinks: links ?? []),
|
self?.push(c)
|
||||||
contents: ChatFolderLinkContents(
|
}, presentController: { [weak self] c in
|
||||||
localFilterId: filterId, title: title,
|
self?.present(c, in: .window(.root))
|
||||||
peers: [],
|
}, completed: {
|
||||||
alreadyMemberPeerIds: Set(),
|
}, linkUpdated: { _ in
|
||||||
memberCounts: [:]
|
})
|
||||||
),
|
} else {
|
||||||
completion: nil
|
let previewScreen = ChatFolderLinkPreviewScreen(
|
||||||
)
|
context: self.context,
|
||||||
self.push(previewScreen)
|
subject: .linkList(folderId: filterId, initialLinks: links ?? []),
|
||||||
|
contents: ChatFolderLinkContents(
|
||||||
|
localFilterId: filterId, title: title,
|
||||||
|
peers: [],
|
||||||
|
alreadyMemberPeerIds: Set(),
|
||||||
|
memberCounts: [:]
|
||||||
|
),
|
||||||
|
completion: nil
|
||||||
|
)
|
||||||
|
self.push(previewScreen)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/*openCreateChatListFolderLink(context: self.context, folderId: filterId, checkIfExists: true, title: title, peerIds: data.includePeers.peers, pushController: { [weak self] c in
|
|
||||||
self?.push(c)
|
|
||||||
}, presentController: { [weak self] c in
|
|
||||||
self?.present(c, in: .window(.root))
|
|
||||||
}, completed: {
|
|
||||||
}, linkUpdated: { _ in
|
|
||||||
})*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func navigateToFolder(folderId: Int32, completion: @escaping () -> Void) {
|
public func navigateToFolder(folderId: Int32, completion: @escaping () -> Void) {
|
||||||
|
@ -14,6 +14,10 @@ import ContextUI
|
|||||||
import AnimationCache
|
import AnimationCache
|
||||||
import MultiAnimationRenderer
|
import MultiAnimationRenderer
|
||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
|
import ActionPanelComponent
|
||||||
|
import ComponentDisplayAdapters
|
||||||
|
import ComponentFlow
|
||||||
|
import ChatFolderLinkPreviewScreen
|
||||||
|
|
||||||
public enum ChatListContainerNodeFilter: Equatable {
|
public enum ChatListContainerNodeFilter: Equatable {
|
||||||
case all
|
case all
|
||||||
@ -316,6 +320,14 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class ChatListContainerItemNode: ASDisplayNode {
|
private final class ChatListContainerItemNode: ASDisplayNode {
|
||||||
|
private final class TopPanelItem {
|
||||||
|
let view = ComponentView<Empty>()
|
||||||
|
var size: CGSize?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let animationCache: AnimationCache
|
private let animationCache: AnimationCache
|
||||||
private let animationRenderer: MultiAnimationRenderer
|
private let animationRenderer: MultiAnimationRenderer
|
||||||
@ -332,6 +344,13 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
|||||||
private var shimmerNodeOffset: CGFloat = 0.0
|
private var shimmerNodeOffset: CGFloat = 0.0
|
||||||
let listNode: ChatListNode
|
let listNode: ChatListNode
|
||||||
|
|
||||||
|
private var topPanel: TopPanelItem?
|
||||||
|
|
||||||
|
private var pollFilterUpdatesDisposable: Disposable?
|
||||||
|
private var chatFilterUpdatesDisposable: Disposable?
|
||||||
|
|
||||||
|
private var chatFolderUpdates: ChatFolderUpdates?
|
||||||
|
|
||||||
private(set) var validLayout: (size: CGSize, insets: UIEdgeInsets, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat)?
|
private(set) var validLayout: (size: CGSize, insets: UIEdgeInsets, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat)?
|
||||||
|
|
||||||
init(context: AccountContext, location: ChatListControllerLocation, filter: ChatListFilter?, chatListMode: ChatListNodeMode, previewing: Bool, isInlineMode: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, becameEmpty: @escaping (ChatListFilter?) -> Void, emptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) {
|
init(context: AccountContext, location: ChatListControllerLocation, filter: ChatListFilter?, chatListMode: ChatListNodeMode, previewing: Bool, isInlineMode: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, becameEmpty: @escaping (ChatListFilter?) -> Void, emptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) {
|
||||||
@ -456,7 +475,40 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
|||||||
if let (size, insets, _, _, _, _) = strongSelf.validLayout, let emptyShimmerEffectNode = strongSelf.emptyShimmerEffectNode {
|
if let (size, insets, _, _, _, _) = strongSelf.validLayout, let emptyShimmerEffectNode = strongSelf.emptyShimmerEffectNode {
|
||||||
strongSelf.layoutEmptyShimmerEffectNode(node: emptyShimmerEffectNode, size: size, insets: insets, verticalOffset: offset + strongSelf.shimmerNodeOffset, transition: transition)
|
strongSelf.layoutEmptyShimmerEffectNode(node: emptyShimmerEffectNode, size: size, insets: insets, verticalOffset: offset + strongSelf.shimmerNodeOffset, transition: transition)
|
||||||
}
|
}
|
||||||
|
strongSelf.layoutAdditionalPanels(transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let filter, case let .filter(id, _, _, data) = filter, data.isShared {
|
||||||
|
self.pollFilterUpdatesDisposable = self.context.engine.peers.pollChatFolderUpdates(folderId: id).start()
|
||||||
|
self.chatFilterUpdatesDisposable = (self.context.engine.peers.subscribedChatFolderUpdates(folderId: id)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var update = false
|
||||||
|
if let result, result.availableChatsToJoin != 0 {
|
||||||
|
if self.chatFolderUpdates?.availableChatsToJoin != result.availableChatsToJoin {
|
||||||
|
update = true
|
||||||
|
}
|
||||||
|
self.chatFolderUpdates = result
|
||||||
|
} else {
|
||||||
|
if self.chatFolderUpdates != nil {
|
||||||
|
self.chatFolderUpdates = nil
|
||||||
|
update = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if update {
|
||||||
|
if let (size, insets, visualNavigationHeight, originalNavigationHeight, inlineNavigationLocation, inlineNavigationTransitionFraction) = self.validLayout {
|
||||||
|
self.updateLayout(size: size, insets: insets, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, transition: .animated(duration: 0.4, curve: .spring))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.pollFilterUpdatesDisposable?.dispose()
|
||||||
|
self.chatFilterUpdatesDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func layoutEmptyShimmerEffectNode(node: ChatListShimmerNode, size: CGSize, insets: UIEdgeInsets, verticalOffset: CGFloat, transition: ContainedViewLayoutTransition) {
|
private func layoutEmptyShimmerEffectNode(node: ChatListShimmerNode, size: CGSize, insets: UIEdgeInsets, verticalOffset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
@ -464,6 +516,32 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
|||||||
transition.updateFrameAdditive(node: node, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: size))
|
transition.updateFrameAdditive(node: node, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func layoutAdditionalPanels(transition: ContainedViewLayoutTransition) {
|
||||||
|
guard let (size, insets, visualNavigationHeight, _, _, _) = self.validLayout, let offset = self.floatingHeaderOffset else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = size
|
||||||
|
let _ = insets
|
||||||
|
|
||||||
|
if let topPanel = self.topPanel, let topPanelSize = topPanel.size {
|
||||||
|
let minY: CGFloat = visualNavigationHeight - 44.0 + topPanelSize.height
|
||||||
|
|
||||||
|
if let topPanelView = topPanel.view.view {
|
||||||
|
var animateIn = false
|
||||||
|
var topPanelTransition = transition
|
||||||
|
if topPanelView.bounds.isEmpty {
|
||||||
|
topPanelTransition = .immediate
|
||||||
|
animateIn = true
|
||||||
|
}
|
||||||
|
topPanelTransition.updateFrame(view: topPanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: max(minY, offset - topPanelSize.height)), size: topPanelSize))
|
||||||
|
if animateIn {
|
||||||
|
transition.animatePositionAdditive(layer: topPanelView.layer, offset: CGPoint(x: 0.0, y: -topPanelView.bounds.height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updatePresentationData(_ presentationData: PresentationData) {
|
func updatePresentationData(_ presentationData: PresentationData) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
|
||||||
@ -479,17 +557,85 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
|||||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayout = (size, insets, visualNavigationHeight, originalNavigationHeight, inlineNavigationLocation, inlineNavigationTransitionFraction)
|
self.validLayout = (size, insets, visualNavigationHeight, originalNavigationHeight, inlineNavigationLocation, inlineNavigationTransitionFraction)
|
||||||
|
|
||||||
|
var listInsets = insets
|
||||||
|
var additionalTopInset: CGFloat = 0.0
|
||||||
|
|
||||||
|
if let chatFolderUpdates = self.chatFolderUpdates {
|
||||||
|
let topPanel: TopPanelItem
|
||||||
|
var topPanelTransition = Transition(transition)
|
||||||
|
if let current = self.topPanel {
|
||||||
|
topPanel = current
|
||||||
|
} else {
|
||||||
|
topPanelTransition = .immediate
|
||||||
|
topPanel = TopPanelItem()
|
||||||
|
self.topPanel = topPanel
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let title: String
|
||||||
|
if chatFolderUpdates.availableChatsToJoin == 1 {
|
||||||
|
title = "1 New Chat Available"
|
||||||
|
} else {
|
||||||
|
title = "\(chatFolderUpdates.availableChatsToJoin) New Chats Available"
|
||||||
|
}
|
||||||
|
|
||||||
|
let topPanelHeight: CGFloat = 44.0
|
||||||
|
|
||||||
|
let _ = topPanel.view.update(
|
||||||
|
transition: topPanelTransition,
|
||||||
|
component: AnyComponent(ActionPanelComponent(
|
||||||
|
theme: self.presentationData.theme,
|
||||||
|
title: title,
|
||||||
|
action: { [weak self] in
|
||||||
|
guard let self, let chatFolderUpdates = self.chatFolderUpdates else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.listNode.push?(ChatFolderLinkPreviewScreen(context: self.context, subject: .updates(chatFolderUpdates), contents: chatFolderUpdates.chatFolderLinkContents))
|
||||||
|
},
|
||||||
|
dismissAction: { [weak self] in
|
||||||
|
guard let self, let chatFolderUpdates = self.chatFolderUpdates else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = self.context.engine.peers.hideChatFolderUpdates(folderId: chatFolderUpdates.folderId).start()
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: size.width, height: topPanelHeight)
|
||||||
|
)
|
||||||
|
if let topPanelView = topPanel.view.view {
|
||||||
|
if topPanelView.superview == nil {
|
||||||
|
self.view.addSubview(topPanelView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
topPanel.size = CGSize(width: size.width, height: topPanelHeight)
|
||||||
|
listInsets.top += topPanelHeight
|
||||||
|
additionalTopInset += topPanelHeight
|
||||||
|
} else {
|
||||||
|
if let topPanel = self.topPanel {
|
||||||
|
self.topPanel = nil
|
||||||
|
if let topPanelView = topPanel.view.view {
|
||||||
|
transition.updatePosition(layer: topPanelView.layer, position: CGPoint(x: topPanelView.layer.position.x, y: topPanelView.layer.position.y - topPanelView.layer.bounds.height), completion: { [weak topPanelView] _ in
|
||||||
|
topPanelView?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: insets, duration: duration, curve: curve)
|
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: listInsets, duration: duration, curve: curve)
|
||||||
|
|
||||||
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
self.listNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: visualNavigationHeight, originalTopInset: originalNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction)
|
self.listNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: visualNavigationHeight + additionalTopInset, originalTopInset: originalNavigationHeight + additionalTopInset, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction)
|
||||||
|
|
||||||
if let emptyNode = self.emptyNode {
|
if let emptyNode = self.emptyNode {
|
||||||
let emptyNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
let emptyNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: listInsets.top), size: CGSize(width: size.width, height: size.height - listInsets.top - listInsets.bottom))
|
||||||
transition.updateFrame(node: emptyNode, frame: emptyNodeFrame)
|
transition.updateFrame(node: emptyNode, frame: emptyNodeFrame)
|
||||||
emptyNode.updateLayout(size: emptyNodeFrame.size, transition: transition)
|
emptyNode.updateLayout(size: emptyNodeFrame.size, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.layoutAdditionalPanels(transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ private final class ChatListFilterPresetControllerArguments {
|
|||||||
let openLink: (ExportedChatFolderLink) -> Void
|
let openLink: (ExportedChatFolderLink) -> Void
|
||||||
let removeLink: (ExportedChatFolderLink) -> Void
|
let removeLink: (ExportedChatFolderLink) -> Void
|
||||||
let linkContextAction: (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void
|
let linkContextAction: (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void
|
||||||
|
let peerContextAction: (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
@ -59,7 +60,8 @@ private final class ChatListFilterPresetControllerArguments {
|
|||||||
createLink: @escaping () -> Void,
|
createLink: @escaping () -> Void,
|
||||||
openLink: @escaping (ExportedChatFolderLink) -> Void,
|
openLink: @escaping (ExportedChatFolderLink) -> Void,
|
||||||
removeLink: @escaping (ExportedChatFolderLink) -> Void,
|
removeLink: @escaping (ExportedChatFolderLink) -> Void,
|
||||||
linkContextAction: @escaping (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void
|
linkContextAction: @escaping (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void,
|
||||||
|
peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.updateState = updateState
|
self.updateState = updateState
|
||||||
@ -77,6 +79,7 @@ private final class ChatListFilterPresetControllerArguments {
|
|||||||
self.openLink = openLink
|
self.openLink = openLink
|
||||||
self.removeLink = removeLink
|
self.removeLink = removeLink
|
||||||
self.linkContextAction = linkContextAction
|
self.linkContextAction = linkContextAction
|
||||||
|
self.peerContextAction = peerContextAction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,6 +517,12 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
|||||||
arguments.setItemIdWithRevealedOptions(lhs.flatMap { .peer($0) }, rhs.flatMap { .peer($0) })
|
arguments.setItemIdWithRevealedOptions(lhs.flatMap { .peer($0) }, rhs.flatMap { .peer($0) })
|
||||||
}, removePeer: { id in
|
}, removePeer: { id in
|
||||||
arguments.deleteIncludePeer(id)
|
arguments.deleteIncludePeer(id)
|
||||||
|
}, contextAction: { sourceNode, gesture in
|
||||||
|
guard let peer = peer.peer else {
|
||||||
|
gesture?.cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
arguments.peerContextAction(peer, sourceNode, gesture, nil)
|
||||||
})
|
})
|
||||||
case let .excludePeer(_, peer, isRevealed):
|
case let .excludePeer(_, peer, isRevealed):
|
||||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: peer.chatMainPeer!, height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: {
|
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: peer.chatMainPeer!, height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: {
|
||||||
@ -522,6 +531,12 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
|||||||
arguments.setItemIdWithRevealedOptions(lhs.flatMap { .peer($0) }, rhs.flatMap { .peer($0) })
|
arguments.setItemIdWithRevealedOptions(lhs.flatMap { .peer($0) }, rhs.flatMap { .peer($0) })
|
||||||
}, removePeer: { id in
|
}, removePeer: { id in
|
||||||
arguments.deleteExcludePeer(id)
|
arguments.deleteExcludePeer(id)
|
||||||
|
}, contextAction: { sourceNode, gesture in
|
||||||
|
guard let peer = peer.peer else {
|
||||||
|
gesture?.cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
arguments.peerContextAction(peer, sourceNode, gesture, nil)
|
||||||
})
|
})
|
||||||
case let .includeExpand(text):
|
case let .includeExpand(text):
|
||||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(presentationData.theme), title: text, sectionId: self.section, editing: false, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(presentationData.theme), title: text, sectionId: self.section, editing: false, action: {
|
||||||
@ -1472,6 +1487,32 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
|||||||
|
|
||||||
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||||
presentInGlobalOverlayImpl?(contextController)
|
presentInGlobalOverlayImpl?(contextController)
|
||||||
|
},
|
||||||
|
peerContextAction: { peer, node, gesture, location in
|
||||||
|
let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true))
|
||||||
|
chatController.canReadHistory.set(false)
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
var items: [ContextMenuItem] = []
|
||||||
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Delete, textColor: .destructive, icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
||||||
|
}, action: { _, f in
|
||||||
|
f(.dismissWithoutContent)
|
||||||
|
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
if let index = state.additionallyExcludePeers.firstIndex(of: peer.id) {
|
||||||
|
state.additionallyExcludePeers.remove(at: index)
|
||||||
|
}
|
||||||
|
if let index = state.additionallyIncludePeers.firstIndex(of: peer.id) {
|
||||||
|
state.additionallyIncludePeers.remove(at: index)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
|
||||||
|
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||||
|
presentInGlobalOverlayImpl?(contextController)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1798,3 +1839,31 @@ private final class InviteLinkContextReferenceContentSource: ContextReferenceCon
|
|||||||
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
|
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||||
|
let controller: ViewController
|
||||||
|
weak var sourceNode: ASDisplayNode?
|
||||||
|
|
||||||
|
let navigationController: NavigationController? = nil
|
||||||
|
|
||||||
|
let passthroughTouches: Bool = true
|
||||||
|
|
||||||
|
init(controller: ViewController, sourceNode: ASDisplayNode?) {
|
||||||
|
self.controller = controller
|
||||||
|
self.sourceNode = sourceNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionInfo() -> ContextControllerTakeControllerInfo? {
|
||||||
|
let sourceNode = self.sourceNode
|
||||||
|
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
|
||||||
|
if let sourceNode = sourceNode {
|
||||||
|
return (sourceNode.view, sourceNode.bounds)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func animatedIn() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1669,6 +1669,30 @@ public final class ChatListNode: ListView {
|
|||||||
|
|
||||||
let currentPeerId: EnginePeer.Id = context.account.peerId
|
let currentPeerId: EnginePeer.Id = context.account.peerId
|
||||||
|
|
||||||
|
|
||||||
|
/*let emptyInitialView = ChatListNodeView(
|
||||||
|
originalList: EngineChatList(
|
||||||
|
items: [],
|
||||||
|
groupItems: [],
|
||||||
|
additionalItems: [],
|
||||||
|
hasEarlier: false,
|
||||||
|
hasLater: false,
|
||||||
|
isLoading: false
|
||||||
|
),
|
||||||
|
filteredEntries: [ChatListNodeEntry.HeaderEntry],
|
||||||
|
isLoading: false,
|
||||||
|
filter: nil
|
||||||
|
)
|
||||||
|
let _ = previousView.swap(emptyInitialView)
|
||||||
|
|
||||||
|
let _ = (preparedChatListNodeViewTransition(from: nil, to: emptyInitialView, reason: .initial, previewing: previewing, disableAnimations: disableAnimations, account: context.account, scrollPosition: nil, searchMode: false)
|
||||||
|
|> map { mappedChatListNodeViewListTransition(context: context, nodeInteraction: nodeInteraction, location: location, filterData: nil, mode: mode, isPeerEnabled: nil, transition: $0) }).start(next: { [weak self] value in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = self.enqueueTransition(value).start()
|
||||||
|
})*/
|
||||||
|
|
||||||
let chatListNodeViewTransition = combineLatest(
|
let chatListNodeViewTransition = combineLatest(
|
||||||
queue: viewProcessingQueue,
|
queue: viewProcessingQueue,
|
||||||
hideArchivedFolderByDefault,
|
hideArchivedFolderByDefault,
|
||||||
@ -1949,7 +1973,7 @@ public final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isEmpty {
|
if isEmpty {
|
||||||
entries = []
|
entries = [.HeaderEntry]
|
||||||
}
|
}
|
||||||
|
|
||||||
let processedView = ChatListNodeView(originalList: update.list, filteredEntries: entries, isLoading: isLoading, filter: filter)
|
let processedView = ChatListNodeView(originalList: update.list, filteredEntries: entries, isLoading: isLoading, filter: filter)
|
||||||
@ -1964,6 +1988,8 @@ public final class ChatListNode: ListView {
|
|||||||
if previous.filteredEntries.count == 1 {
|
if previous.filteredEntries.count == 1 {
|
||||||
if case .HoleEntry = previous.filteredEntries[0] {
|
if case .HoleEntry = previous.filteredEntries[0] {
|
||||||
previousWasEmptyOrSingleHole = true
|
previousWasEmptyOrSingleHole = true
|
||||||
|
} else if case .HeaderEntry = previous.filteredEntries[0] {
|
||||||
|
previousWasEmptyOrSingleHole = true
|
||||||
}
|
}
|
||||||
} else if previous.filteredEntries.isEmpty && previous.isLoading {
|
} else if previous.filteredEntries.isEmpty && previous.isLoading {
|
||||||
previousWasEmptyOrSingleHole = true
|
previousWasEmptyOrSingleHole = true
|
||||||
@ -2655,7 +2681,9 @@ public final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func pollFilterUpdates() {
|
private func pollFilterUpdates() {
|
||||||
guard let chatListFilter, case let .filter(id, _, _, data) = chatListFilter, data.isShared else {
|
self.chatFolderUpdates.set(.single(nil))
|
||||||
|
|
||||||
|
/*guard let chatListFilter, case let .filter(id, _, _, data) = chatListFilter, data.isShared else {
|
||||||
self.chatFolderUpdates.set(.single(nil))
|
self.chatFolderUpdates.set(.single(nil))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2666,7 +2694,7 @@ public final class ChatListNode: ListView {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.chatFolderUpdates.set(.single(result))
|
self.chatFolderUpdates.set(.single(result))
|
||||||
})
|
})*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private func resetFilter() {
|
private func resetFilter() {
|
||||||
@ -2960,7 +2988,8 @@ public final class ChatListNode: ListView {
|
|||||||
scrollToItem = ListViewScrollToItem(index: 0, position: .top(offset), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
|
scrollToItem = ListViewScrollToItem(index: 0, position: .top(offset), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.transaction(deleteIndices: transition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: transition.stationaryItemRange, updateOpaqueState: ChatListOpaqueTransactionState(chatListView: transition.chatListView), completion: completion)
|
let updatedOpaqueState: Any? = ChatListOpaqueTransactionState(chatListView: transition.chatListView)
|
||||||
|
self.transaction(deleteIndices: transition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: transition.stationaryItemRange, updateOpaqueState: updatedOpaqueState, completion: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2968,6 +2997,8 @@ public final class ChatListNode: ListView {
|
|||||||
switch self.visibleContentOffset() {
|
switch self.visibleContentOffset() {
|
||||||
case let .known(value) where abs(value) < navigationBarSearchContentHeight - 1.0:
|
case let .known(value) where abs(value) < navigationBarSearchContentHeight - 1.0:
|
||||||
return false
|
return false
|
||||||
|
case .none:
|
||||||
|
return false
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -3070,6 +3101,7 @@ public final class ChatListNode: ListView {
|
|||||||
|
|
||||||
if !self.dequeuedInitialTransitionOnLayout {
|
if !self.dequeuedInitialTransitionOnLayout {
|
||||||
self.dequeuedInitialTransitionOnLayout = true
|
self.dequeuedInitialTransitionOnLayout = true
|
||||||
|
|
||||||
self.dequeueTransition()
|
self.dequeueTransition()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,24 +311,49 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
|||||||
if let currentBadge = self.currentBadge, currentBadge.badge == badge {
|
if let currentBadge = self.currentBadge, currentBadge.badge == badge {
|
||||||
badgeImage = currentBadge.image
|
badgeImage = currentBadge.image
|
||||||
} else {
|
} else {
|
||||||
let badgeTextColor: UIColor = presentationData.theme.list.itemCheckColors.foregroundColor
|
switch badge.style {
|
||||||
let badgeString = NSAttributedString(string: badge.value, font: Font.semibold(11.0), textColor: badgeTextColor)
|
case .badge:
|
||||||
let badgeTextBounds = badgeString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil)
|
let badgeTextColor: UIColor = presentationData.theme.list.itemCheckColors.foregroundColor
|
||||||
let badgeSideInset: CGFloat = 3.0
|
let badgeString = NSAttributedString(string: badge.value, font: Font.regular(13.0), textColor: badgeTextColor)
|
||||||
let badgeVerticalInset: CGFloat = 1.0
|
let badgeTextBounds = badgeString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil)
|
||||||
let badgeBackgroundSize = CGSize(width: badgeSideInset * 2.0 + ceil(badgeTextBounds.width), height: badgeVerticalInset * 2.0 + ceil(badgeTextBounds.height))
|
|
||||||
badgeImage = generateImage(badgeBackgroundSize, rotatedContext: { size, context in
|
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
|
||||||
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
|
||||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: 5.0).cgPath)
|
|
||||||
context.fillPath()
|
|
||||||
|
|
||||||
UIGraphicsPushContext(context)
|
let badgeSideInset: CGFloat = 5.0
|
||||||
|
let badgeVerticalInset: CGFloat = 1.0
|
||||||
|
var badgeBackgroundSize = CGSize(width: badgeSideInset * 2.0 + ceil(badgeTextBounds.width), height: badgeVerticalInset * 2.0 + ceil(badgeTextBounds.height))
|
||||||
|
badgeBackgroundSize.width = max(badgeBackgroundSize.width, badgeBackgroundSize.height)
|
||||||
|
badgeImage = generateImage(badgeBackgroundSize, rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||||
|
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: size.height * 0.5).cgPath)
|
||||||
|
context.fillPath()
|
||||||
|
|
||||||
|
UIGraphicsPushContext(context)
|
||||||
|
|
||||||
|
badgeString.draw(at: CGPoint(x: badgeTextBounds.minX + floor((badgeBackgroundSize.width - badgeTextBounds.width) * 0.5), y: badgeTextBounds.minY + badgeVerticalInset))
|
||||||
|
|
||||||
|
UIGraphicsPopContext()
|
||||||
|
})
|
||||||
|
case .label:
|
||||||
|
let badgeTextColor: UIColor = presentationData.theme.list.itemCheckColors.foregroundColor
|
||||||
|
let badgeString = NSAttributedString(string: badge.value, font: Font.semibold(11.0), textColor: badgeTextColor)
|
||||||
|
let badgeTextBounds = badgeString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil)
|
||||||
|
|
||||||
badgeString.draw(at: CGPoint(x: badgeTextBounds.minX + badgeSideInset + UIScreenPixel, y: badgeTextBounds.minY + badgeVerticalInset + UIScreenPixel))
|
let badgeSideInset: CGFloat = 3.0
|
||||||
|
let badgeVerticalInset: CGFloat = 1.0
|
||||||
UIGraphicsPopContext()
|
let badgeBackgroundSize = CGSize(width: badgeSideInset * 2.0 + ceil(badgeTextBounds.width), height: badgeVerticalInset * 2.0 + ceil(badgeTextBounds.height))
|
||||||
})
|
badgeImage = generateImage(badgeBackgroundSize, rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||||
|
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: 5.0).cgPath)
|
||||||
|
context.fillPath()
|
||||||
|
|
||||||
|
UIGraphicsPushContext(context)
|
||||||
|
|
||||||
|
badgeString.draw(at: CGPoint(x: badgeTextBounds.minX + badgeSideInset + UIScreenPixel, y: badgeTextBounds.minY + badgeVerticalInset + UIScreenPixel))
|
||||||
|
|
||||||
|
UIGraphicsPopContext()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let badgeIconNode: ASImageNode
|
let badgeIconNode: ASImageNode
|
||||||
|
@ -504,7 +504,7 @@ func _internal_joinChatFolderLink(account: Account, slug: String, peerIds: [Engi
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class ChatFolderUpdates: Equatable {
|
public final class ChatFolderUpdates: Equatable {
|
||||||
fileprivate let folderId: Int32
|
public let folderId: Int32
|
||||||
fileprivate let title: String
|
fileprivate let title: String
|
||||||
fileprivate let missingPeers: [EnginePeer]
|
fileprivate let missingPeers: [EnginePeer]
|
||||||
fileprivate let memberCounts: [EnginePeer.Id: Int]
|
fileprivate let memberCounts: [EnginePeer.Id: Int]
|
||||||
|
23
submodules/TelegramUI/Components/ActionPanelComponent/BUILD
Normal file
23
submodules/TelegramUI/Components/ActionPanelComponent/BUILD
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "ActionPanelComponent",
|
||||||
|
module_name = "ActionPanelComponent",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/Display:Display",
|
||||||
|
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||||
|
"//submodules/ComponentFlow",
|
||||||
|
"//submodules/AnimatedCountLabelNode",
|
||||||
|
"//submodules/Components/ComponentDisplayAdapters",
|
||||||
|
"//submodules/AppBundle",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
@ -0,0 +1,172 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import TelegramPresentationData
|
||||||
|
import ComponentFlow
|
||||||
|
import ComponentDisplayAdapters
|
||||||
|
import AppBundle
|
||||||
|
|
||||||
|
public final class ActionPanelComponent: Component {
|
||||||
|
public let theme: PresentationTheme
|
||||||
|
public let title: String
|
||||||
|
public let action: () -> Void
|
||||||
|
public let dismissAction: () -> Void
|
||||||
|
|
||||||
|
public init(
|
||||||
|
theme: PresentationTheme,
|
||||||
|
title: String,
|
||||||
|
action: @escaping () -> Void,
|
||||||
|
dismissAction: @escaping () -> Void
|
||||||
|
) {
|
||||||
|
self.theme = theme
|
||||||
|
self.title = title
|
||||||
|
self.action = action
|
||||||
|
self.dismissAction = dismissAction
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: ActionPanelComponent, rhs: ActionPanelComponent) -> Bool {
|
||||||
|
if lhs.theme !== rhs.theme {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.title != rhs.title {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class View: HighlightTrackingButton {
|
||||||
|
private let backgroundView: BlurredBackgroundView
|
||||||
|
private let separatorLayer: SimpleLayer
|
||||||
|
|
||||||
|
private let contentView: UIView
|
||||||
|
private let title = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private let dismissButton: HighlightTrackingButton
|
||||||
|
private let dismissIconView: UIImageView
|
||||||
|
|
||||||
|
private var component: ActionPanelComponent?
|
||||||
|
|
||||||
|
public override init(frame: CGRect) {
|
||||||
|
self.backgroundView = BlurredBackgroundView(color: nil, enableBlur: true)
|
||||||
|
self.backgroundView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.separatorLayer = SimpleLayer()
|
||||||
|
self.contentView = UIView()
|
||||||
|
self.contentView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.dismissButton = HighlightTrackingButton()
|
||||||
|
self.dismissIconView = UIImageView()
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.addSubview(self.backgroundView)
|
||||||
|
self.layer.addSublayer(self.separatorLayer)
|
||||||
|
self.addSubview(self.contentView)
|
||||||
|
|
||||||
|
self.dismissButton.addSubview(self.dismissIconView)
|
||||||
|
self.addSubview(self.dismissButton)
|
||||||
|
|
||||||
|
self.highligthedChanged = { [weak self] highlighted in
|
||||||
|
if let self {
|
||||||
|
if highlighted {
|
||||||
|
self.contentView.layer.removeAnimation(forKey: "opacity")
|
||||||
|
self.contentView.alpha = 0.65
|
||||||
|
} else {
|
||||||
|
self.contentView.alpha = 1.0
|
||||||
|
self.contentView.layer.animateAlpha(from: 0.65, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||||
|
|
||||||
|
self.dismissButton.highligthedChanged = { [weak self] highlighted in
|
||||||
|
if let self {
|
||||||
|
if highlighted {
|
||||||
|
self.dismissButton.layer.removeAnimation(forKey: "opacity")
|
||||||
|
self.dismissButton.alpha = 0.65
|
||||||
|
} else {
|
||||||
|
self.dismissButton.alpha = 1.0
|
||||||
|
self.dismissButton.layer.animateAlpha(from: 0.65, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.dismissButton.addTarget(self, action: #selector(self.dismissPressed), for: .touchUpInside)
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func pressed() {
|
||||||
|
guard let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
component.action()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func dismissPressed() {
|
||||||
|
guard let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
component.dismissAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
return super.hitTest(point, with: event)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: ActionPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
let themeUpdated = self.component?.theme !== component.theme
|
||||||
|
|
||||||
|
self.component = component
|
||||||
|
|
||||||
|
if themeUpdated {
|
||||||
|
self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||||
|
self.separatorLayer.backgroundColor = component.theme.rootController.navigationBar.separatorColor.cgColor
|
||||||
|
|
||||||
|
self.dismissIconView.image = UIImage(bundleImageName: "Chat/Input/Accessory Panels/EncircledCloseButton")?.withRenderingMode(.alwaysTemplate)
|
||||||
|
self.dismissIconView.tintColor = component.theme.rootController.navigationBar.accentTextColor
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
|
self.backgroundView.update(size: availableSize, transition: transition.containedViewLayoutTransition)
|
||||||
|
transition.setFrame(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - UIScreenPixel), size: CGSize(width: availableSize.width, height: UIScreenPixel)))
|
||||||
|
|
||||||
|
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
|
|
||||||
|
let rightInset: CGFloat = 44.0
|
||||||
|
|
||||||
|
let titleSize = self.title.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(Text(text: component.title, font: Font.regular(17.0), color: component.theme.rootController.navigationBar.accentTextColor)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - rightInset, height: availableSize.height)
|
||||||
|
)
|
||||||
|
if let titleView = self.title.view {
|
||||||
|
if titleView.superview == nil {
|
||||||
|
titleView.layer.anchorPoint = CGPoint()
|
||||||
|
self.contentView.addSubview(titleView)
|
||||||
|
}
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5), y: floor((availableSize.height - titleSize.height) * 0.5)), size: titleSize)
|
||||||
|
transition.setPosition(view: titleView, position: titleFrame.origin)
|
||||||
|
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
let dismissButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset, y: 0.0), size: CGSize(width: rightInset, height: availableSize.height))
|
||||||
|
transition.setFrame(view: self.dismissButton, frame: dismissButtonFrame)
|
||||||
|
if let iconImage = self.dismissIconView.image {
|
||||||
|
transition.setFrame(view: self.dismissIconView, frame: CGRect(origin: CGPoint(x: floor((dismissButtonFrame.width - iconImage.size.width) * 0.5), y: floor((dismissButtonFrame.height - iconImage.size.height) * 0.5)), size: iconImage.size))
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeView() -> View {
|
||||||
|
return View(frame: CGRect())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user