mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-05-21 18:20:41 +00:00
Glass
This commit is contained in:
@@ -138,7 +138,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
})))
|
||||
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .reference(AuthorizationContextReferenceContentSource(controller: self, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: .reference(AuthorizationContextReferenceContentSource(controller: self, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
self.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
|
||||
@@ -443,7 +443,7 @@ final class BrowserAddressListComponent: Component {
|
||||
}
|
||||
|
||||
let items = ContextController.Items(content: .list(itemList))
|
||||
let controller = ContextController(
|
||||
let controller = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(BrowserAddressListContextExtractedContentSource(contentView: sourceView)),
|
||||
items: .single(items),
|
||||
|
||||
@@ -273,7 +273,7 @@ public final class BrowserBookmarksScreen: ViewController {
|
||||
})))
|
||||
|
||||
let items = ContextController.Items(content: .list(itemList))
|
||||
let controller = ContextController(
|
||||
let controller = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(BrowserBookmarksContextExtractedContentSource(contentNode: sourceNode)),
|
||||
items: .single(items),
|
||||
|
||||
@@ -1198,7 +1198,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
}
|
||||
|
||||
private func activatePinchPreview(sourceNode: PinchSourceContainerNode) {
|
||||
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: { [weak self] in
|
||||
let pinchController = makePinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: { [weak self] in
|
||||
guard let self else {
|
||||
return CGRect()
|
||||
}
|
||||
|
||||
@@ -1230,7 +1230,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
return ContextController.Items(content: .list(items))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: source, items: items)
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: source, items: items)
|
||||
contextController.dismissed = { [weak content] in
|
||||
if let webContent = content as? BrowserWebContent {
|
||||
webContent.releaseInstantView()
|
||||
@@ -1342,7 +1342,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
return
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items))))
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items))))
|
||||
self.controller?.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
|
||||
@@ -517,7 +517,7 @@ public final class CallListController: TelegramBaseController {
|
||||
}
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .extracted(ExtractedContentSourceImpl(controller: self, sourceNode: buttonNode.contentNode, keepInPlace: false, blurBackground: false)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: .extracted(ExtractedContentSourceImpl(controller: self, sourceNode: buttonNode.contentNode, keepInPlace: false, blurBackground: false)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
self.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
@@ -782,7 +782,7 @@ public final class CallListController: TelegramBaseController {
|
||||
})
|
||||
})))
|
||||
|
||||
let controller = ContextController(presentationData: self.presentationData, source: .reference(CallListTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
let controller = makeContextController(presentationData: self.presentationData, source: .reference(CallListTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||
}
|
||||
|
||||
|
||||
@@ -1285,10 +1285,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 +1873,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 +1891,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 +1908,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 +1926,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 +1960,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 +1995,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 +2011,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)
|
||||
}
|
||||
}
|
||||
@@ -3425,7 +3425,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)
|
||||
})
|
||||
}
|
||||
@@ -3878,7 +3878,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 +3938,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)
|
||||
})
|
||||
}
|
||||
@@ -6206,7 +6206,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 +6408,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)
|
||||
}
|
||||
|
||||
|
||||
@@ -1927,7 +1927,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi
|
||||
})
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
})
|
||||
},
|
||||
@@ -1968,7 +1968,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi
|
||||
})
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
},
|
||||
updateTagColor: { color in
|
||||
|
||||
@@ -1174,7 +1174,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 +1248,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 +1312,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
|
||||
|
||||
@@ -5622,7 +5622,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
f(.default)
|
||||
self.searchScopePromise.set(.channels)
|
||||
})))
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .reference(ChatListSearchReferenceContentSource(sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: .reference(ChatListSearchReferenceContentSource(sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
self.interaction.present(contextController, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -650,7 +650,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
if let current = self.actionsStackNode {
|
||||
actionsStackNode = current
|
||||
|
||||
actionsStackNode.replace(item: ContextControllerActionsListStackItem(
|
||||
actionsStackNode.replace(item: makeContextControllerActionsListStackItem(
|
||||
id: AnyHashable("items"),
|
||||
items: items,
|
||||
reactionItems: nil,
|
||||
@@ -660,7 +660,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
dismissed: nil
|
||||
), animated: !transition.animation.isImmediate)
|
||||
} else {
|
||||
actionsStackNode = ContextControllerActionsStackNode(
|
||||
actionsStackNode = makeContextControllerActionsStackNode(
|
||||
context: component.context,
|
||||
getController: {
|
||||
return nil
|
||||
@@ -681,7 +681,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
||||
}
|
||||
|
||||
actionsStackNode.push(
|
||||
item: ContextControllerActionsListStackItem(
|
||||
item: makeContextControllerActionsListStackItem(
|
||||
id: AnyHashable("items"),
|
||||
items: items,
|
||||
reactionItems: nil,
|
||||
|
||||
@@ -10,7 +10,8 @@ swift_library(
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display"
|
||||
"//submodules/Display",
|
||||
"//submodules/UIKitRuntimeUtils",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import UIKitRuntimeUtils
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
@_silgen_name("UIAnimationDragCoefficient") func UIAnimationDragCoefficient() -> Float
|
||||
@@ -84,7 +85,7 @@ public extension ComponentTransition.Animation {
|
||||
}
|
||||
|
||||
public extension ComponentTransition {
|
||||
func animateView(allowUserInteraction: Bool = false, delay: Double = 0.0, _ f: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
func animateView(allowUserInteraction: Bool = true, delay: Double = 0.0, _ f: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
switch self.animation {
|
||||
case .none:
|
||||
f()
|
||||
@@ -94,9 +95,17 @@ public extension ComponentTransition {
|
||||
if allowUserInteraction {
|
||||
options.insert(.allowUserInteraction)
|
||||
}
|
||||
UIView.animate(withDuration: duration, delay: delay, options: options, animations: {
|
||||
f()
|
||||
}, completion: completion)
|
||||
if case .spring = curve {
|
||||
CALayer.push(CALayerSpringParametersOverride())
|
||||
UIView.animate(withDuration: duration, delay: delay, usingSpringWithDamping: 500.0, initialSpringVelocity: 0.0, options: options, animations: {
|
||||
f()
|
||||
}, completion: completion)
|
||||
CALayer.popSpringParametersOverride()
|
||||
} else {
|
||||
UIView.animate(withDuration: duration, delay: delay, options: options, animations: {
|
||||
f()
|
||||
}, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,7 +636,13 @@ public class ContactsController: ViewController {
|
||||
})))
|
||||
return items
|
||||
}
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
|
||||
var sourceView = sourceView
|
||||
if let navigationBarComponentView = self.contactsNode.navigationBarView.view as? ChatListNavigationBar.View, let headerContentView = navigationBarComponentView.headerContent.view as? ChatListHeaderComponent.View, let value = headerContentView.navigationButtonContextContainer(sourceView: sourceView) {
|
||||
sourceView = value
|
||||
}
|
||||
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
self.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
@@ -800,7 +806,7 @@ public class ContactsController: ViewController {
|
||||
})
|
||||
})))
|
||||
|
||||
let controller = ContextController(presentationData: self.presentationData, source: .reference(ContactsTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
let controller = makeContextController(presentationData: self.presentationData, source: .reference(ContactsTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||
}
|
||||
|
||||
|
||||
@@ -467,12 +467,12 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
let items = contactContextMenuItems(context: self.context, peerId: peer.id, contactsController: contactsController, isStories: isStories) |> map { ContextController.Items(content: .list($0)) }
|
||||
|
||||
if isStories, let node = node?.subnodes?.first(where: { $0 is ContextExtractedContentContainingNode }) as? ContextExtractedContentContainingNode {
|
||||
let controller = ContextController(presentationData: self.presentationData, source: .extracted(ContactContextExtractedContentSource(sourceNode: node, shouldBeDismissed: .single(false))), items: items, recognizer: nil, gesture: gesture)
|
||||
let controller = makeContextController(presentationData: self.presentationData, source: .extracted(ContactContextExtractedContentSource(sourceNode: node, shouldBeDismissed: .single(false))), items: items, recognizer: nil, gesture: gesture)
|
||||
contactsController.presentInGlobalOverlay(controller)
|
||||
} else {
|
||||
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil)
|
||||
chatController.canReadHistory.set(false)
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: items, gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: items, gesture: gesture)
|
||||
contactsController.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,39 @@ import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
|
||||
public enum PeekControllerContentPresentation {
|
||||
case contained
|
||||
case freeform
|
||||
}
|
||||
|
||||
public enum PeerControllerMenuActivation {
|
||||
case drag
|
||||
case press
|
||||
}
|
||||
|
||||
public protocol PeekControllerContent {
|
||||
func presentation() -> PeekControllerContentPresentation
|
||||
func menuActivation() -> PeerControllerMenuActivation
|
||||
func menuItems() -> [ContextMenuItem]
|
||||
func node() -> PeekControllerContentNode & ASDisplayNode
|
||||
|
||||
func topAccessoryNode() -> ASDisplayNode?
|
||||
func fullScreenAccessoryNode(blurView: UIVisualEffectView) -> (PeekControllerAccessoryNode & ASDisplayNode)?
|
||||
|
||||
func isEqual(to: PeekControllerContent) -> Bool
|
||||
}
|
||||
|
||||
public protocol PeekControllerContentNode {
|
||||
func ready() -> Signal<Bool, NoError>
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize
|
||||
}
|
||||
|
||||
public protocol PeekControllerAccessoryNode {
|
||||
var dismiss: () -> Void { get set }
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
|
||||
public final class PeekControllerTheme {
|
||||
public let isDark: Bool
|
||||
public let menuBackgroundColor: UIColor
|
||||
@@ -30,115 +63,32 @@ extension PeekControllerTheme {
|
||||
}
|
||||
}
|
||||
|
||||
public final class PeekController: ViewController, ContextControllerProtocol {
|
||||
public var useComplexItemsTransitionAnimation: Bool = false
|
||||
public var immediateItemsTransitionAnimation = false
|
||||
|
||||
public func getActionsMinHeight() -> ContextController.ActionsHeight? {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, animated: Bool) {
|
||||
}
|
||||
|
||||
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, previousActionsTransition: ContextController.PreviousActionsTransition) {
|
||||
}
|
||||
|
||||
public func pushItems(items: Signal<ContextController.Items, NoError>) {
|
||||
self.controllerNode.pushItems(items: items)
|
||||
}
|
||||
|
||||
public func popItems() {
|
||||
self.controllerNode.popItems()
|
||||
}
|
||||
|
||||
private var controllerNode: PeekControllerNode {
|
||||
return self.displayNode as! PeekControllerNode
|
||||
}
|
||||
|
||||
public var contentNode: PeekControllerContentNode & ASDisplayNode {
|
||||
return self.controllerNode.contentNode
|
||||
}
|
||||
|
||||
private let presentationData: PresentationData
|
||||
private let content: PeekControllerContent
|
||||
var sourceView: () -> (UIView, CGRect)?
|
||||
private let activateImmediately: Bool
|
||||
|
||||
public var visibilityUpdated: ((Bool) -> Void)?
|
||||
|
||||
public var getOverlayViews: (() -> [UIView])?
|
||||
|
||||
public var appeared: (() -> Void)?
|
||||
public var disappeared: (() -> Void)?
|
||||
|
||||
private var animatedIn = false
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
override public var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
public init(presentationData: PresentationData, content: PeekControllerContent, sourceView: @escaping () -> (UIView, CGRect)?, activateImmediately: Bool = false) {
|
||||
self.presentationData = presentationData
|
||||
self.content = content
|
||||
self.sourceView = sourceView
|
||||
self.activateImmediately = activateImmediately
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = PeekControllerNode(presentationData: self.presentationData, controller: self, content: self.content, requestDismiss: { [weak self] in
|
||||
self?.dismiss()
|
||||
})
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
private func getSourceRect() -> CGRect {
|
||||
if let (sourceView, sourceRect) = self.sourceView() {
|
||||
return sourceView.convert(sourceRect, to: self.view)
|
||||
} else {
|
||||
let size = self.displayNode.bounds.size
|
||||
return CGRect(origin: CGPoint(x: floor((size.width - 10.0) / 2.0), y: floor((size.height - 10.0) / 2.0)), size: CGSize(width: 10.0, height: 10.0))
|
||||
}
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if !self.animatedIn {
|
||||
self.animatedIn = true
|
||||
self.controllerNode.animateIn(from: self.getSourceRect())
|
||||
|
||||
self.visibilityUpdated?(true)
|
||||
|
||||
if self.activateImmediately {
|
||||
self.controllerNode.activateMenu(immediately: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.visibilityUpdated?(false)
|
||||
self.controllerNode.animateOut(to: self.getSourceRect(), completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
})
|
||||
}
|
||||
|
||||
public func dismiss(result: ContextMenuActionResult, completion: (() -> Void)?) {
|
||||
self.dismiss(completion: completion)
|
||||
}
|
||||
public protocol PeekController: ViewController, ContextControllerProtocol {
|
||||
var visibilityUpdated: ((Bool) -> Void)? { get set }
|
||||
var getOverlayViews: (() -> [UIView])? { get set }
|
||||
var appeared: (() -> Void)? { get set }
|
||||
var disappeared: (() -> Void)? { get set }
|
||||
var sourceView: () -> (UIView, CGRect)? { get set }
|
||||
var contentNode: PeekControllerContentNode & ASDisplayNode { get }
|
||||
}
|
||||
|
||||
public var makePeekControllerImpl: ((
|
||||
_ presentationData: PresentationData,
|
||||
_ content: PeekControllerContent,
|
||||
_ sourceView: @escaping () -> (UIView, CGRect)?,
|
||||
_ activateImmediately: Bool
|
||||
) -> PeekController)?
|
||||
|
||||
public func makePeekController(
|
||||
presentationData: PresentationData,
|
||||
content: PeekControllerContent,
|
||||
sourceView: @escaping () -> (UIView, CGRect)?,
|
||||
activateImmediately: Bool = false
|
||||
) -> PeekController {
|
||||
return makePeekControllerImpl!(
|
||||
presentationData,
|
||||
content,
|
||||
sourceView,
|
||||
activateImmediately
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
|
||||
public enum PeekControllerContentPresentation {
|
||||
case contained
|
||||
case freeform
|
||||
}
|
||||
|
||||
public enum PeerControllerMenuActivation {
|
||||
case drag
|
||||
case press
|
||||
}
|
||||
|
||||
public protocol PeekControllerContent {
|
||||
func presentation() -> PeekControllerContentPresentation
|
||||
func menuActivation() -> PeerControllerMenuActivation
|
||||
func menuItems() -> [ContextMenuItem]
|
||||
func node() -> PeekControllerContentNode & ASDisplayNode
|
||||
|
||||
func topAccessoryNode() -> ASDisplayNode?
|
||||
func fullScreenAccessoryNode(blurView: UIVisualEffectView) -> (PeekControllerAccessoryNode & ASDisplayNode)?
|
||||
|
||||
func isEqual(to: PeekControllerContent) -> Bool
|
||||
}
|
||||
|
||||
public protocol PeekControllerContentNode {
|
||||
func ready() -> Signal<Bool, NoError>
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize
|
||||
}
|
||||
|
||||
public protocol PeekControllerAccessoryNode {
|
||||
var dismiss: () -> Void { get set }
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
@@ -4,6 +4,13 @@ import SwiftSignalKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
|
||||
public protocol PeekControllerNodeProtocol: AnyObject {
|
||||
func applyDraggingOffset(_ offset: CGPoint)
|
||||
func activateMenu(immediately: Bool)
|
||||
func endDragging(_ location: CGPoint)
|
||||
func updateContent(content: PeekControllerContent)
|
||||
}
|
||||
|
||||
private func traceDeceleratingScrollView(_ view: UIView, at point: CGPoint) -> Bool {
|
||||
if view.bounds.contains(point), let view = view as? UIScrollView, view.isDecelerating {
|
||||
return true
|
||||
@@ -108,7 +115,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
if let _ = self.tapLocation, let menuActivation = self.menuActivation, case .press = menuActivation {
|
||||
if let presentedController = self.presentedController {
|
||||
if presentedController.isNodeLoaded {
|
||||
(presentedController.displayNode as? PeekControllerNode)?.activateMenu()
|
||||
(presentedController.displayNode as? PeekControllerNodeProtocol)?.activateMenu(immediately: false)
|
||||
}
|
||||
self.menuActivation = nil
|
||||
// self.presentedController = nil
|
||||
@@ -148,7 +155,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
self.state = .ended
|
||||
} else {
|
||||
if let presentedController = self.presentedController, presentedController.isNodeLoaded, let location = touches.first?.location(in: presentedController.view) {
|
||||
(presentedController.displayNode as? PeekControllerNode)?.endDragging(location)
|
||||
(presentedController.displayNode as? PeekControllerNodeProtocol)?.endDragging(location)
|
||||
self.presentedController = nil
|
||||
self.menuActivation = nil
|
||||
}
|
||||
@@ -184,7 +191,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
if let presentedController = self.presentedController, self.menuActivation == nil {
|
||||
if presentedController.isNodeLoaded {
|
||||
let touchLocation = touch.location(in: presentedController.view)
|
||||
(presentedController.displayNode as? PeekControllerNode)?.applyDraggingOffset(touchLocation)
|
||||
(presentedController.displayNode as? PeekControllerNodeProtocol)?.applyDraggingOffset(touchLocation)
|
||||
}
|
||||
} else if let menuActivation = self.menuActivation, let presentedController = self.presentedController {
|
||||
switch menuActivation {
|
||||
@@ -201,7 +208,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
if touch.force >= 2.5 {
|
||||
if presentedController.isNodeLoaded {
|
||||
(presentedController.displayNode as? PeekControllerNode)?.activateMenu()
|
||||
(presentedController.displayNode as? PeekControllerNodeProtocol)?.activateMenu(immediately: false)
|
||||
self.menuActivation = nil
|
||||
self.presentedController = nil
|
||||
self.candidateContent = nil
|
||||
@@ -268,7 +275,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
presentedController.sourceView = {
|
||||
return (sourceView, sourceRect)
|
||||
}
|
||||
(presentedController.displayNode as? PeekControllerNode)?.updateContent(content: content)
|
||||
(presentedController.displayNode as? PeekControllerNodeProtocol)?.updateContent(content: content)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -277,7 +284,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
if forceActivate {
|
||||
strongSelf.candidateContent = nil
|
||||
if case .press = content.menuActivation() {
|
||||
(presentedController.displayNode as? PeekControllerNode)?.activateMenu()
|
||||
(presentedController.displayNode as? PeekControllerNodeProtocol)?.activateMenu(immediately: false)
|
||||
}
|
||||
} else {
|
||||
strongSelf.candidateContent = (sourceView, sourceRect, content)
|
||||
|
||||
@@ -3,480 +3,25 @@ import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import TextSelectionNode
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import UIKitRuntimeUtils
|
||||
|
||||
final class PinchSourceGesture: UIPinchGestureRecognizer {
|
||||
private final class Target {
|
||||
var updated: (() -> Void)?
|
||||
|
||||
@objc func onGesture(_ gesture: UIPinchGestureRecognizer) {
|
||||
self.updated?()
|
||||
}
|
||||
}
|
||||
|
||||
private let target: Target
|
||||
|
||||
private(set) var currentTransform: (CGFloat, CGPoint, CGPoint)?
|
||||
|
||||
var began: (() -> Void)?
|
||||
var updated: ((CGFloat, CGPoint, CGPoint) -> Void)?
|
||||
var ended: (() -> Void)?
|
||||
|
||||
private var initialLocation: CGPoint?
|
||||
private var pinchLocation = CGPoint()
|
||||
private var currentOffset = CGPoint()
|
||||
|
||||
private var currentNumberOfTouches = 0
|
||||
|
||||
init() {
|
||||
self.target = Target()
|
||||
|
||||
super.init(target: self.target, action: #selector(self.target.onGesture(_:)))
|
||||
|
||||
self.target.updated = { [weak self] in
|
||||
self?.gestureUpdated()
|
||||
}
|
||||
}
|
||||
|
||||
override func reset() {
|
||||
super.reset()
|
||||
|
||||
self.currentNumberOfTouches = 0
|
||||
self.initialLocation = nil
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
|
||||
//self.currentTouches.formUnion(touches)
|
||||
}
|
||||
|
||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesEnded(touches, with: event)
|
||||
}
|
||||
|
||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesCancelled(touches, with: event)
|
||||
}
|
||||
|
||||
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesMoved(touches, with: event)
|
||||
}
|
||||
|
||||
private func gestureUpdated() {
|
||||
switch self.state {
|
||||
case .began:
|
||||
self.currentOffset = CGPoint()
|
||||
|
||||
let pinchLocation = self.location(in: self.view)
|
||||
self.pinchLocation = pinchLocation
|
||||
self.initialLocation = pinchLocation
|
||||
let scale = max(1.0, self.scale)
|
||||
self.currentTransform = (scale, self.pinchLocation, self.currentOffset)
|
||||
|
||||
self.currentNumberOfTouches = self.numberOfTouches
|
||||
|
||||
self.began?()
|
||||
case .changed:
|
||||
let locationSum = self.location(in: self.view)
|
||||
|
||||
if self.numberOfTouches < 2 && self.currentNumberOfTouches >= 2 {
|
||||
self.initialLocation = CGPoint(x: locationSum.x - self.currentOffset.x, y: locationSum.y - self.currentOffset.y)
|
||||
}
|
||||
self.currentNumberOfTouches = self.numberOfTouches
|
||||
|
||||
if let initialLocation = self.initialLocation {
|
||||
self.currentOffset = CGPoint(x: locationSum.x - initialLocation.x, y: locationSum.y - initialLocation.y)
|
||||
}
|
||||
if let (scale, pinchLocation, _) = self.currentTransform {
|
||||
self.currentTransform = (scale, pinchLocation, self.currentOffset)
|
||||
self.updated?(scale, pinchLocation, self.currentOffset)
|
||||
}
|
||||
|
||||
let scale = max(1.0, self.scale)
|
||||
self.currentTransform = (scale, self.pinchLocation, self.currentOffset)
|
||||
self.updated?(scale, self.pinchLocation, self.currentOffset)
|
||||
case .ended, .cancelled:
|
||||
self.ended?()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
public protocol PinchController: ViewController {
|
||||
func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
|
||||
private func cancelContextGestures(node: ASDisplayNode) {
|
||||
if let node = node as? ContextControllerSourceNode {
|
||||
node.cancelGesture()
|
||||
}
|
||||
public var makePinchControllerImpl: ((
|
||||
_ sourceNode: PinchSourceContainerNode,
|
||||
_ disableScreenshots: Bool,
|
||||
_ getContentAreaInScreenSpace: @escaping () -> CGRect
|
||||
) -> PinchController)?
|
||||
|
||||
if let supernode = node.supernode {
|
||||
cancelContextGestures(node: supernode)
|
||||
}
|
||||
}
|
||||
|
||||
private func cancelContextGestures(view: UIView) {
|
||||
if let gestureRecognizers = view.gestureRecognizers {
|
||||
for recognizer in gestureRecognizers {
|
||||
if let recognizer = recognizer as? InteractiveTransitionGestureRecognizer {
|
||||
recognizer.cancel()
|
||||
} else if let recognizer = recognizer as? WindowPanRecognizer {
|
||||
recognizer.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let superview = view.superview {
|
||||
cancelContextGestures(view: superview)
|
||||
}
|
||||
}
|
||||
|
||||
public final class PinchSourceContainerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
public let contentNode: ASDisplayNode
|
||||
public var contentRect: CGRect = CGRect()
|
||||
private(set) var naturalContentFrame: CGRect?
|
||||
|
||||
fileprivate let gesture: PinchSourceGesture
|
||||
|
||||
public var isPinchGestureEnabled: Bool = true {
|
||||
didSet {
|
||||
if self.isPinchGestureEnabled != oldValue {
|
||||
self.gesture.isEnabled = self.isPinchGestureEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var maxPinchScale: CGFloat = 10.0
|
||||
|
||||
private var isActive: Bool = false
|
||||
|
||||
public var activate: ((PinchSourceContainerNode) -> Void)?
|
||||
public var scaleUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
public var animatedOut: (() -> Void)?
|
||||
var deactivate: (() -> Void)?
|
||||
public var deactivated: (() -> Void)?
|
||||
var updated: ((CGFloat, CGPoint, CGPoint) -> Void)?
|
||||
|
||||
override public init() {
|
||||
self.gesture = PinchSourceGesture()
|
||||
self.contentNode = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
|
||||
self.gesture.began = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
cancelContextGestures(node: strongSelf)
|
||||
cancelContextGestures(view: strongSelf.view)
|
||||
strongSelf.isActive = true
|
||||
|
||||
strongSelf.activate?(strongSelf)
|
||||
}
|
||||
|
||||
self.gesture.ended = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.isActive = false
|
||||
strongSelf.deactivate?()
|
||||
strongSelf.deactivated?()
|
||||
}
|
||||
|
||||
self.gesture.updated = { [weak self] scale, pinchLocation, offset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updated?(min(scale, strongSelf.maxPinchScale), pinchLocation, offset)
|
||||
strongSelf.scaleUpdated?(min(scale, strongSelf.maxPinchScale), .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.view.addGestureRecognizer(self.gesture)
|
||||
self.view.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return false
|
||||
}
|
||||
return strongSelf.isActive
|
||||
}
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public func update(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
let contentFrame = CGRect(origin: CGPoint(), size: size)
|
||||
self.naturalContentFrame = contentFrame
|
||||
if !self.isActive {
|
||||
transition.updateFrame(node: self.contentNode, frame: contentFrame)
|
||||
}
|
||||
}
|
||||
|
||||
func restoreToNaturalSize() {
|
||||
guard let naturalContentFrame = self.naturalContentFrame else {
|
||||
return
|
||||
}
|
||||
self.contentNode.frame = naturalContentFrame
|
||||
}
|
||||
}
|
||||
|
||||
private final class PinchControllerNode: ViewControllerTracingNode {
|
||||
private weak var controller: PinchController?
|
||||
|
||||
private var initialSourceFrame: CGRect?
|
||||
|
||||
private let clippingNode: ASDisplayNode
|
||||
private let scrollingContainer: ASDisplayNode
|
||||
|
||||
private let sourceNode: PinchSourceContainerNode
|
||||
private let disableScreenshots: Bool
|
||||
private let getContentAreaInScreenSpace: () -> CGRect
|
||||
|
||||
private let dimNode: ASDisplayNode
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var isAnimatingOut: Bool = false
|
||||
|
||||
private var hapticFeedback: HapticFeedback?
|
||||
|
||||
init(controller: PinchController, sourceNode: PinchSourceContainerNode, disableScreenshots: Bool, getContentAreaInScreenSpace: @escaping () -> CGRect) {
|
||||
self.controller = controller
|
||||
self.sourceNode = sourceNode
|
||||
self.disableScreenshots = disableScreenshots
|
||||
self.getContentAreaInScreenSpace = getContentAreaInScreenSpace
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
||||
self.dimNode.alpha = 0.0
|
||||
|
||||
self.clippingNode = ASDisplayNode()
|
||||
self.clippingNode.clipsToBounds = true
|
||||
|
||||
self.scrollingContainer = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.clippingNode)
|
||||
self.clippingNode.addSubnode(self.scrollingContainer)
|
||||
|
||||
self.sourceNode.deactivate = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.controller?.dismiss()
|
||||
}
|
||||
|
||||
self.sourceNode.updated = { [weak self] scale, pinchLocation, offset in
|
||||
guard let strongSelf = self, let initialSourceFrame = strongSelf.initialSourceFrame else {
|
||||
return
|
||||
}
|
||||
strongSelf.dimNode.alpha = max(0.0, min(1.0, scale - 1.0))
|
||||
|
||||
let pinchOffset = CGPoint(
|
||||
x: pinchLocation.x - initialSourceFrame.width / 2.0,
|
||||
y: pinchLocation.y - initialSourceFrame.height / 2.0
|
||||
)
|
||||
|
||||
var transform = CATransform3DIdentity
|
||||
transform = CATransform3DTranslate(transform, offset.x - pinchOffset.x * (scale - 1.0), offset.y - pinchOffset.y * (scale - 1.0), 0.0)
|
||||
transform = CATransform3DScale(transform, scale, scale, 0.0)
|
||||
|
||||
strongSelf.sourceNode.contentNode.transform = transform
|
||||
}
|
||||
|
||||
if self.disableScreenshots {
|
||||
setLayerDisableScreenshots(self.layer, true)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
}
|
||||
|
||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?) {
|
||||
if self.isAnimatingOut {
|
||||
return
|
||||
}
|
||||
|
||||
self.validLayout = layout
|
||||
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
let convertedFrame = convertFrame(self.sourceNode.bounds, from: self.sourceNode.view, to: self.view)
|
||||
self.sourceNode.contentNode.frame = convertedFrame
|
||||
self.initialSourceFrame = convertedFrame
|
||||
self.scrollingContainer.addSubnode(self.sourceNode.contentNode)
|
||||
|
||||
var updatedContentAreaInScreenSpace = self.getContentAreaInScreenSpace()
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
|
||||
self.clippingNode.layer.animateFrame(from: updatedContentAreaInScreenSpace, to: self.clippingNode.frame, duration: 0.18 * 1.0, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: updatedContentAreaInScreenSpace.minY, to: 0.0, duration: 0.18 * 1.0, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
}
|
||||
|
||||
func animateOut(completion: @escaping () -> Void) {
|
||||
self.isAnimatingOut = true
|
||||
|
||||
let performCompletion: () -> Void = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.isAnimatingOut = false
|
||||
|
||||
strongSelf.sourceNode.restoreToNaturalSize()
|
||||
strongSelf.sourceNode.addSubnode(strongSelf.sourceNode.contentNode)
|
||||
|
||||
strongSelf.sourceNode.animatedOut?()
|
||||
|
||||
completion()
|
||||
}
|
||||
|
||||
let convertedFrame = convertFrame(self.sourceNode.bounds, from: self.sourceNode.view, to: self.view)
|
||||
self.sourceNode.contentNode.frame = convertedFrame
|
||||
self.initialSourceFrame = convertedFrame
|
||||
|
||||
if let (scale, pinchLocation, offset) = self.sourceNode.gesture.currentTransform, let initialSourceFrame = self.initialSourceFrame {
|
||||
let duration = 0.3
|
||||
let transitionCurve: ContainedViewLayoutTransitionCurve = .easeInOut
|
||||
|
||||
var updatedContentAreaInScreenSpace = self.getContentAreaInScreenSpace()
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
|
||||
self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: updatedContentAreaInScreenSpace, duration: duration * 1.0, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: updatedContentAreaInScreenSpace.minY, duration: duration * 1.0, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: .spring)
|
||||
if self.hapticFeedback == nil {
|
||||
self.hapticFeedback = HapticFeedback()
|
||||
}
|
||||
self.hapticFeedback?.prepareImpact(.light)
|
||||
self.hapticFeedback?.impact(.light)
|
||||
|
||||
self.sourceNode.scaleUpdated?(1.0, transition)
|
||||
|
||||
let pinchOffset = CGPoint(
|
||||
x: pinchLocation.x - initialSourceFrame.width / 2.0,
|
||||
y: pinchLocation.y - initialSourceFrame.height / 2.0
|
||||
)
|
||||
|
||||
var transform = CATransform3DIdentity
|
||||
transform = CATransform3DScale(transform, scale, scale, 0.0)
|
||||
|
||||
self.sourceNode.contentNode.transform = CATransform3DIdentity
|
||||
self.sourceNode.contentNode.position = CGPoint(x: initialSourceFrame.midX, y: initialSourceFrame.midY)
|
||||
self.sourceNode.contentNode.layer.animateSpring(from: scale as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration * 1.2, damping: 110.0)
|
||||
self.sourceNode.contentNode.layer.animatePosition(from: CGPoint(x: offset.x - pinchOffset.x * (scale - 1.0), y: offset.y - pinchOffset.y * (scale - 1.0)), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true, force: true, completion: { _ in
|
||||
performCompletion()
|
||||
})
|
||||
|
||||
let dimNodeTransition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: transitionCurve)
|
||||
dimNodeTransition.updateAlpha(node: self.dimNode, alpha: 0.0)
|
||||
} else {
|
||||
performCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition) {
|
||||
if self.isAnimatingOut {
|
||||
self.scrollingContainer.bounds = self.scrollingContainer.bounds.offsetBy(dx: 0.0, dy: offset.y)
|
||||
transition.animateOffsetAdditive(node: self.scrollingContainer, offset: -offset.y)
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public final class PinchController: ViewController, StandalonePresentableController {
|
||||
private let _ready = Promise<Bool>()
|
||||
override public var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
private let sourceNode: PinchSourceContainerNode
|
||||
private let disableScreenshots: Bool
|
||||
private let getContentAreaInScreenSpace: () -> CGRect
|
||||
|
||||
private var wasDismissed = false
|
||||
|
||||
private var controllerNode: PinchControllerNode {
|
||||
return self.displayNode as! PinchControllerNode
|
||||
}
|
||||
|
||||
public init(sourceNode: PinchSourceContainerNode, disableScreenshots: Bool = false, getContentAreaInScreenSpace: @escaping () -> CGRect) {
|
||||
self.sourceNode = sourceNode
|
||||
self.disableScreenshots = disableScreenshots
|
||||
self.getContentAreaInScreenSpace = getContentAreaInScreenSpace
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
self.lockOrientation = true
|
||||
self.blocksBackgroundWhenInOverlay = true
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = PinchControllerNode(controller: self, sourceNode: self.sourceNode, disableScreenshots: self.disableScreenshots, getContentAreaInScreenSpace: self.getContentAreaInScreenSpace)
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self._ready.set(.single(true))
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.controllerNode.updateLayout(layout: layout, transition: transition, previousActionsContainerNode: nil)
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
if self.ignoreAppearanceMethodInvocations() {
|
||||
return
|
||||
}
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
self.controllerNode.animateIn()
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
if !self.wasDismissed {
|
||||
self.wasDismissed = true
|
||||
self.controllerNode.animateOut(completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition) {
|
||||
self.controllerNode.addRelativeContentOffset(offset, transition: transition)
|
||||
}
|
||||
public func makePinchController(
|
||||
sourceNode: PinchSourceContainerNode,
|
||||
disableScreenshots: Bool = false,
|
||||
getContentAreaInScreenSpace: @escaping () -> CGRect
|
||||
) -> PinchController {
|
||||
return makePinchControllerImpl!(
|
||||
sourceNode,
|
||||
disableScreenshots,
|
||||
getContentAreaInScreenSpace
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
|
||||
private func cancelContextGestures(node: ASDisplayNode) {
|
||||
if let node = node as? ContextControllerSourceNode {
|
||||
node.cancelGesture()
|
||||
}
|
||||
|
||||
if let supernode = node.supernode {
|
||||
cancelContextGestures(node: supernode)
|
||||
}
|
||||
}
|
||||
|
||||
private func cancelContextGestures(view: UIView) {
|
||||
if let gestureRecognizers = view.gestureRecognizers {
|
||||
for recognizer in gestureRecognizers {
|
||||
if let recognizer = recognizer as? InteractiveTransitionGestureRecognizer {
|
||||
recognizer.cancel()
|
||||
} else if let recognizer = recognizer as? WindowPanRecognizer {
|
||||
recognizer.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let superview = view.superview {
|
||||
cancelContextGestures(view: superview)
|
||||
}
|
||||
}
|
||||
|
||||
public final class PinchSourceGesture: UIPinchGestureRecognizer {
|
||||
private final class Target {
|
||||
var updated: (() -> Void)?
|
||||
|
||||
@objc func onGesture(_ gesture: UIPinchGestureRecognizer) {
|
||||
self.updated?()
|
||||
}
|
||||
}
|
||||
|
||||
private let target: Target
|
||||
|
||||
public private(set) var currentTransform: (CGFloat, CGPoint, CGPoint)?
|
||||
|
||||
public var began: (() -> Void)?
|
||||
public var updated: ((CGFloat, CGPoint, CGPoint) -> Void)?
|
||||
public var ended: (() -> Void)?
|
||||
|
||||
private var initialLocation: CGPoint?
|
||||
private var pinchLocation = CGPoint()
|
||||
private var currentOffset = CGPoint()
|
||||
|
||||
private var currentNumberOfTouches = 0
|
||||
|
||||
public init() {
|
||||
self.target = Target()
|
||||
|
||||
super.init(target: self.target, action: #selector(self.target.onGesture(_:)))
|
||||
|
||||
self.target.updated = { [weak self] in
|
||||
self?.gestureUpdated()
|
||||
}
|
||||
}
|
||||
|
||||
override public func reset() {
|
||||
super.reset()
|
||||
|
||||
self.currentNumberOfTouches = 0
|
||||
self.initialLocation = nil
|
||||
}
|
||||
|
||||
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
|
||||
//self.currentTouches.formUnion(touches)
|
||||
}
|
||||
|
||||
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesEnded(touches, with: event)
|
||||
}
|
||||
|
||||
override public func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesCancelled(touches, with: event)
|
||||
}
|
||||
|
||||
override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesMoved(touches, with: event)
|
||||
}
|
||||
|
||||
private func gestureUpdated() {
|
||||
switch self.state {
|
||||
case .began:
|
||||
self.currentOffset = CGPoint()
|
||||
|
||||
let pinchLocation = self.location(in: self.view)
|
||||
self.pinchLocation = pinchLocation
|
||||
self.initialLocation = pinchLocation
|
||||
let scale = max(1.0, self.scale)
|
||||
self.currentTransform = (scale, self.pinchLocation, self.currentOffset)
|
||||
|
||||
self.currentNumberOfTouches = self.numberOfTouches
|
||||
|
||||
self.began?()
|
||||
case .changed:
|
||||
let locationSum = self.location(in: self.view)
|
||||
|
||||
if self.numberOfTouches < 2 && self.currentNumberOfTouches >= 2 {
|
||||
self.initialLocation = CGPoint(x: locationSum.x - self.currentOffset.x, y: locationSum.y - self.currentOffset.y)
|
||||
}
|
||||
self.currentNumberOfTouches = self.numberOfTouches
|
||||
|
||||
if let initialLocation = self.initialLocation {
|
||||
self.currentOffset = CGPoint(x: locationSum.x - initialLocation.x, y: locationSum.y - initialLocation.y)
|
||||
}
|
||||
if let (scale, pinchLocation, _) = self.currentTransform {
|
||||
self.currentTransform = (scale, pinchLocation, self.currentOffset)
|
||||
self.updated?(scale, pinchLocation, self.currentOffset)
|
||||
}
|
||||
|
||||
let scale = max(1.0, self.scale)
|
||||
self.currentTransform = (scale, self.pinchLocation, self.currentOffset)
|
||||
self.updated?(scale, self.pinchLocation, self.currentOffset)
|
||||
case .ended, .cancelled:
|
||||
self.ended?()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final class PinchSourceContainerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
public let contentNode: ASDisplayNode
|
||||
public var contentRect: CGRect = CGRect()
|
||||
private(set) var naturalContentFrame: CGRect?
|
||||
|
||||
public let gesture: PinchSourceGesture
|
||||
|
||||
public var isPinchGestureEnabled: Bool = true {
|
||||
didSet {
|
||||
if self.isPinchGestureEnabled != oldValue {
|
||||
self.gesture.isEnabled = self.isPinchGestureEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var maxPinchScale: CGFloat = 10.0
|
||||
|
||||
private var isActive: Bool = false
|
||||
|
||||
public var activate: ((PinchSourceContainerNode) -> Void)?
|
||||
public var scaleUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
public var animatedOut: (() -> Void)?
|
||||
public var deactivate: (() -> Void)?
|
||||
public var deactivated: (() -> Void)?
|
||||
public var updated: ((CGFloat, CGPoint, CGPoint) -> Void)?
|
||||
|
||||
override public init() {
|
||||
self.gesture = PinchSourceGesture()
|
||||
self.contentNode = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
|
||||
self.gesture.began = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
cancelContextGestures(node: strongSelf)
|
||||
cancelContextGestures(view: strongSelf.view)
|
||||
strongSelf.isActive = true
|
||||
|
||||
strongSelf.activate?(strongSelf)
|
||||
}
|
||||
|
||||
self.gesture.ended = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.isActive = false
|
||||
strongSelf.deactivate?()
|
||||
strongSelf.deactivated?()
|
||||
}
|
||||
|
||||
self.gesture.updated = { [weak self] scale, pinchLocation, offset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updated?(min(scale, strongSelf.maxPinchScale), pinchLocation, offset)
|
||||
strongSelf.scaleUpdated?(min(scale, strongSelf.maxPinchScale), .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.view.addGestureRecognizer(self.gesture)
|
||||
self.view.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return false
|
||||
}
|
||||
return strongSelf.isActive
|
||||
}
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public func update(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
let contentFrame = CGRect(origin: CGPoint(), size: size)
|
||||
self.naturalContentFrame = contentFrame
|
||||
if !self.isActive {
|
||||
transition.updateFrame(node: self.contentNode, frame: contentFrame)
|
||||
}
|
||||
}
|
||||
|
||||
public func restoreToNaturalSize() {
|
||||
guard let naturalContentFrame = self.naturalContentFrame else {
|
||||
return
|
||||
}
|
||||
self.contentNode.frame = naturalContentFrame
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public final class ContextContentContainerNode: ASDisplayNode {
|
||||
switch contentNode {
|
||||
case .reference:
|
||||
break
|
||||
case .extracted:
|
||||
case .extracted, .extractedContainer:
|
||||
break
|
||||
case let .controller(controller):
|
||||
transition.updatePosition(node: controller, position: CGPoint(x: scaledSize.width / 2.0, y: scaledSize.height / 2.0))
|
||||
|
||||
@@ -39,6 +39,24 @@ public final class ContextExtractedContentContainingNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public enum ContextExtractableContainerState {
|
||||
public enum ExtractionState {
|
||||
case animatedOut
|
||||
case animatedIn
|
||||
}
|
||||
|
||||
case normal
|
||||
case extracted(size: CGSize, cornerRadius: CGFloat, state: ExtractionState)
|
||||
}
|
||||
|
||||
public protocol ContextExtractableContainer: UIView {
|
||||
typealias State = ContextExtractableContainerState
|
||||
|
||||
var extractableContentView: UIView { get }
|
||||
|
||||
func updateState(state: State, transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
|
||||
public final class ContextExtractedContentContainingView: UIView {
|
||||
public let contentView: ContextExtractedContentView
|
||||
public var contentRect: CGRect = CGRect()
|
||||
@@ -152,5 +170,6 @@ public final class ContextControllerContentNode: ASDisplayNode {
|
||||
public enum ContextContentNode {
|
||||
case reference(view: UIView)
|
||||
case extracted(node: ContextExtractedContentContainingNode, keepInPlace: Bool)
|
||||
case extractedContainer(container: ContextExtractableContainer)
|
||||
case controller(ContextControllerContentNode)
|
||||
}
|
||||
|
||||
@@ -192,6 +192,8 @@ public protocol NavigationBar: ASDisplayNode {
|
||||
func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition)
|
||||
|
||||
func updateEdgeEffectExtension(value: CGFloat, transition: ContainedViewLayoutTransition)
|
||||
|
||||
func navigationButtonContextContainer(sourceView: UIView) -> ContextExtractableContainer?
|
||||
}
|
||||
|
||||
public var defaultNavigationBarImpl: ((NavigationBarPresentationData) -> NavigationBar)?
|
||||
|
||||
@@ -989,7 +989,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
)
|
||||
]
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme)
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: UIScreen.main.bounds, customPosition: CGPoint(x: 7.0, y: 3.0))), items: .single(ContextController.Items(content: .list(items))))
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: UIScreen.main.bounds, customPosition: CGPoint(x: 7.0, y: 3.0))), items: .single(ContextController.Items(content: .list(items))))
|
||||
self.present(contextController)
|
||||
}
|
||||
|
||||
@@ -3482,7 +3482,7 @@ public final class DrawingToolsInteraction {
|
||||
}
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme)
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: CGRect(origin: .zero, size: CGSize(width: validLayout.size.width, height: validLayout.size.height - (validLayout.inputHeight ?? 0.0))), customPosition: CGPoint(x: 0.0, y: 1.0))), items: .single(ContextController.Items(content: .list(items))))
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: CGRect(origin: .zero, size: CGSize(width: validLayout.size.width, height: validLayout.size.height - (validLayout.inputHeight ?? 0.0))), customPosition: CGPoint(x: 0.0, y: 1.0))), items: .single(ContextController.Items(content: .list(items))))
|
||||
self.present(contextController, .window(.root), nil)
|
||||
self.currentFontPicker = contextController
|
||||
contextController.view.disablesInteractiveKeyboardGestureRecognizer = true
|
||||
|
||||
@@ -707,7 +707,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
return nil
|
||||
}, present: { [weak self] content, sourceView, sourceRect in
|
||||
if let strongSelf = self {
|
||||
let controller = PeekController(presentationData: strongSelf.presentationData, content: content, sourceView: {
|
||||
let controller = makePeekController(presentationData: strongSelf.presentationData, content: content, sourceView: {
|
||||
return (sourceView, sourceRect)
|
||||
})
|
||||
strongSelf.peekController = controller
|
||||
|
||||
@@ -756,7 +756,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
guard let controller = self.baseNavigationController()?.topViewController as? ViewController else {
|
||||
return
|
||||
}
|
||||
let contextController = ContextController(presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.moreBarButton.referenceNode, actionsOnTop: false)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.moreBarButton.referenceNode, actionsOnTop: false)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
|
||||
@@ -3254,7 +3254,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
})
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: sourceNode, actionsOnTop: actionsOnTop)), items: items |> map { items in
|
||||
let contextController = makeContextController(presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: sourceNode, actionsOnTop: actionsOnTop)), items: items |> map { items in
|
||||
if !items.topItems.isEmpty {
|
||||
return ContextController.Items(id: AnyHashable(0), content: .twoLists(items.items, items.topItems))
|
||||
} else {
|
||||
|
||||
@@ -265,7 +265,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, ASScroll
|
||||
return nil
|
||||
}, present: { [weak self] content, sourceView, sourceRect in
|
||||
if let strongSelf = self {
|
||||
let controller = PeekController(presentationData: strongSelf.presentationData, content: content, sourceView: {
|
||||
let controller = makePeekController(presentationData: strongSelf.presentationData, content: content, sourceView: {
|
||||
return (sourceView, sourceRect)
|
||||
})
|
||||
controller.visibilityUpdated = { [weak self] visible in
|
||||
|
||||
@@ -628,7 +628,7 @@ final class InstantPageControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
guard let strongSelf = self, let controller = strongSelf.controller else {
|
||||
return
|
||||
}
|
||||
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
let pinchController = makePinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
guard let strongSelf = self else {
|
||||
return CGRect()
|
||||
}
|
||||
|
||||
@@ -501,7 +501,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
|
||||
})
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
}, peerAction: { peer, isEnabled in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
@@ -518,7 +518,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
self.controller?.presentInGlobalOverlay(contextController)
|
||||
}, copyLink: { [weak self] invite in
|
||||
UIPasteboard.general.string = invite.link
|
||||
|
||||
@@ -602,7 +602,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
}, createLink: {
|
||||
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: nil, completion: { invite in
|
||||
@@ -820,7 +820,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
}, openAdmin: { admin in
|
||||
let controller = inviteLinkListController(context: context, peerId: peerId, admin: admin)
|
||||
|
||||
@@ -808,7 +808,7 @@ public final class InviteLinkViewController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
self?.controller?.presentInGlobalOverlay(contextController)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -268,7 +268,7 @@ public func inviteRequestsController(context: AccountContext, updatedPresentatio
|
||||
// dismissPromise.set(true)
|
||||
// }
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -457,7 +457,7 @@ public final class InviteRequestsSearchContainerNode: SearchDisplayControllerCon
|
||||
// dismissPromise.set(true)
|
||||
// }
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlay(contextController)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -339,7 +339,7 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode {
|
||||
return
|
||||
}
|
||||
strongSelf.avatarListNode?.controlsContainerNode.alpha = 0.0
|
||||
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
let pinchController = makePinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
return UIScreen.main.bounds
|
||||
})
|
||||
item.context.sharedContext.mainWindow?.presentInGlobalOverlay(pinchController)
|
||||
|
||||
@@ -2388,7 +2388,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
)
|
||||
|
||||
self.titleView.isHighlighted = true
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
presentationData: self.presentationData,
|
||||
source: .reference(MediaPickerContextReferenceContentSource(controller: self, sourceView: self.titleView)),
|
||||
items: .single(ContextController.Items(content: .custom(content))),
|
||||
@@ -2923,7 +2923,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
self?.presentFilePicker()
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .reference(MediaPickerContextReferenceContentSource(controller: self, sourceView: view)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: .reference(MediaPickerContextReferenceContentSource(controller: self, sourceView: view)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
self.presentInGlobalOverlay(contextController)
|
||||
|
||||
return
|
||||
@@ -3084,7 +3084,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
return ContextController.Items(content: .list(items))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .reference(MediaPickerContextReferenceContentSource(controller: self, sourceView: view)), items: items, gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: .reference(MediaPickerContextReferenceContentSource(controller: self, sourceView: view)), items: items, gesture: gesture)
|
||||
self.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1668,7 +1668,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
||||
})
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
}, manageInviteLinks: {
|
||||
let controller = inviteLinkListController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, admin: nil)
|
||||
|
||||
@@ -488,7 +488,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil)
|
||||
chatController.canReadHistory.set(false)
|
||||
let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: peerNearbyContextMenuItems(context: context, peerId: peer.id, present: { c in
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: peerNearbyContextMenuItems(context: context, peerId: peer.id, present: { c in
|
||||
presentControllerImpl?(c, nil)
|
||||
}) |> map { ContextController.Items(content: .list($0), animationCache: nil) }, gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
|
||||
@@ -422,7 +422,7 @@ public func storageUsageExceptionsScreen(
|
||||
let items: Signal<ContextController.Items, NoError> = .single(ContextController.Items(content: .list(subItems)))
|
||||
let source: ContextContentSource = .reference(StorageUsageExceptionsContextReferenceContentSource(sourceView: sourceNode.labelNode.view))
|
||||
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: source,
|
||||
items: items,
|
||||
|
||||
@@ -689,7 +689,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController, nil)
|
||||
})
|
||||
}, colorContextAction: { isCurrent, reference, accentColor, node, gesture in
|
||||
@@ -938,7 +938,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme
|
||||
}
|
||||
}
|
||||
}
|
||||
let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController, nil)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -779,7 +779,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController, nil)
|
||||
})
|
||||
}, colorContextAction: { isCurrent, reference, accentColor, node, gesture in
|
||||
@@ -1028,7 +1028,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
}
|
||||
}
|
||||
}
|
||||
let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController, nil)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -592,7 +592,7 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
||||
])
|
||||
return ContextController.Items(content: .list(items), animationCache: nil)
|
||||
}
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(ShareContextReferenceContentSource(sourceNode: node, customPosition: CGPoint(x: 0.0, y: fromForeignApp ? -116.0 : 0.0))), items: items, gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(ShareContextReferenceContentSource(sourceNode: node, customPosition: CGPoint(x: 0.0, y: fromForeignApp ? -116.0 : 0.0))), items: items, gesture: gesture)
|
||||
contextController.immediateItemsTransitionAnimation = true
|
||||
strongSelf.present?(contextController)
|
||||
}
|
||||
|
||||
@@ -2440,7 +2440,7 @@ public func channelStatsController(
|
||||
})
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .extracted(ChannelStatsContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .extracted(ChannelStatsContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
dismissAllTooltipsImpl = { [weak controller] in
|
||||
|
||||
@@ -556,7 +556,7 @@ public func messageStatsController(context: AccountContext, updatedPresentationD
|
||||
})
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .extracted(ChannelStatsContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .extracted(ChannelStatsContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
return controller
|
||||
|
||||
@@ -638,7 +638,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.hideMainPreviewIcon()
|
||||
|
||||
let controller = PeekController(presentationData: strongSelf.presentationData, content: content, sourceView: {
|
||||
let controller = makePeekController(presentationData: strongSelf.presentationData, content: content, sourceView: {
|
||||
return (sourceView, sourceRect)
|
||||
})
|
||||
controller.visibilityUpdated = { [weak self] visible in
|
||||
@@ -1228,7 +1228,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .reference(StickerPackContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: .reference(StickerPackContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
self.presentInGlobalOverlay(contextController, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -596,7 +596,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi
|
||||
let items = self.contextMenuSpeedItems(scheduleTooltip: { change in
|
||||
scheduledTooltip = change
|
||||
})
|
||||
let contextController = ContextController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items, gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items, gesture: gesture)
|
||||
contextController.dismissed = { [weak self] in
|
||||
if let scheduledTooltip, let self, let rate = self.playbackBaseRate {
|
||||
self.setRate?(rate, scheduledTooltip)
|
||||
|
||||
@@ -655,7 +655,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData.withUpdated(theme: defaultDarkPresentationTheme), source: .reference(ReferenceContentSource(sourceView: anchorView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
let contextController = makeContextController(presentationData: presentationData.withUpdated(theme: defaultDarkPresentationTheme), source: .reference(ReferenceContentSource(sourceView: anchorView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
).minSize(CGSize(width: 44.0, height: 44.0)).tagged(moreButtonTag))
|
||||
|
||||
@@ -1112,7 +1112,7 @@ final class VideoChatParticipantsComponent: Component {
|
||||
}
|
||||
self.isPinchToZoomActive = true
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
let pinchController = makePinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
return UIScreen.main.bounds
|
||||
})
|
||||
component.call.accountContext.sharedContext.mainWindow?.presentInGlobalOverlay(pinchController)
|
||||
|
||||
@@ -596,7 +596,7 @@ extension VideoChatScreenComponent.View {
|
||||
}
|
||||
|
||||
let presentationData = currentCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(VoiceChatContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(VoiceChatContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
|
||||
@@ -336,7 +336,7 @@ extension VideoChatScreenComponent.View {
|
||||
}
|
||||
|
||||
let presentationData = currentCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(ParticipantExtractedContentSource(contentView: sourceView)),
|
||||
items: items |> map { items in
|
||||
@@ -394,7 +394,7 @@ extension VideoChatScreenComponent.View {
|
||||
let items = itemsForEntry()
|
||||
|
||||
let presentationData = currentCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(ParticipantExtractedContentSource(contentView: sourceView)),
|
||||
items: .single(ContextController.Items(content: .list(items))),
|
||||
|
||||
@@ -904,7 +904,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
|
||||
}
|
||||
strongSelf.setControlsHidden(true, animated: false)
|
||||
strongSelf.controlsHidden?(true)
|
||||
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
let pinchController = makePinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
return UIScreen.main.bounds
|
||||
})
|
||||
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(pinchController)
|
||||
|
||||
@@ -462,7 +462,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
||||
return
|
||||
}
|
||||
strongSelf.avatarListNode?.controlsContainerNode.alpha = 0.0
|
||||
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
let pinchController = makePinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
return UIScreen.main.bounds
|
||||
})
|
||||
item.context.sharedContext.mainWindow?.presentInGlobalOverlay(pinchController)
|
||||
|
||||
@@ -99,7 +99,7 @@ final class VoiceChatPeerProfileNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
strongSelf.avatarListNode.controlsContainerNode.alpha = 0.0
|
||||
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
let pinchController = makePinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
return UIScreen.main.bounds
|
||||
})
|
||||
context.sharedContext.mainWindow?.presentInGlobalOverlay(pinchController)
|
||||
|
||||
@@ -121,6 +121,9 @@ extension TelegramUser {
|
||||
if (flags2 & (1 << 16)) != 0 {
|
||||
botFlags.insert(.hasForum)
|
||||
}
|
||||
if (flags2 & (1 << 17)) != 0 {
|
||||
botFlags.insert(.forumManagedByUser)
|
||||
}
|
||||
botInfo = BotUserInfo(flags: botFlags, inlinePlaceholder: botInlinePlaceholder)
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ public struct BotUserInfoFlags: OptionSet {
|
||||
public static let isBusiness = BotUserInfoFlags(rawValue: (1 << 6))
|
||||
public static let hasWebApp = BotUserInfoFlags(rawValue: (1 << 7))
|
||||
public static let hasForum = BotUserInfoFlags(rawValue: (1 << 8))
|
||||
public static let forumManagedByUser = BotUserInfoFlags(rawValue: (1 << 9))
|
||||
}
|
||||
|
||||
public struct BotUserInfo: PostboxCoding, Equatable {
|
||||
|
||||
@@ -513,6 +513,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController",
|
||||
"//submodules/TelegramUI/Components/CocoonInfoScreen",
|
||||
"//submodules/TelegramUI/Components/ContextControllerImpl",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
"//build-system:ios_sim_arm64": [],
|
||||
|
||||
@@ -1344,7 +1344,7 @@ public class AdsInfoScreen: ViewController {
|
||||
})))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -1227,7 +1227,7 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
let items = ContextController.Items(content: .list(itemList), tip: .collageReordering)
|
||||
let controller = ContextController(
|
||||
let controller = makeContextController(
|
||||
presentationData: presentationData.withUpdated(theme: defaultDarkColorPresentationTheme),
|
||||
source: .extracted(CollageContextExtractedContentSource(contentView: sourceView)),
|
||||
items: .single(items),
|
||||
|
||||
+1
-1
@@ -1163,7 +1163,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
return
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(actions))), recognizer: recognizer, gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(actions))), recognizer: recognizer, gesture: gesture)
|
||||
controller.window?.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ swift_library(
|
||||
"//submodules/AvatarNode",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/UndoUI",
|
||||
"//submodules/TelegramUI/Components/ContextControllerImpl",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
+1
@@ -12,6 +12,7 @@ import TelegramStringFormatting
|
||||
import AvatarNode
|
||||
import AccountContext
|
||||
import UndoUI
|
||||
import ContextControllerImpl
|
||||
|
||||
public final class ChatSendAsPeerListContextItem: ContextMenuCustomItem {
|
||||
let context: AccountContext
|
||||
|
||||
+1
-1
@@ -1378,7 +1378,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, actionsOnTop: false)), items: .single(ContextController.Items(id: AnyHashable(0), content: .list(items))), gesture: nil)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, actionsOnTop: false)), items: .single(ContextController.Items(id: AnyHashable(0), content: .list(items))), gesture: nil)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
|
||||
+18
-8
@@ -46,10 +46,10 @@ public final class ChatSideTopicsPanel: Component {
|
||||
case top
|
||||
}
|
||||
|
||||
public enum Kind {
|
||||
public enum Kind: Equatable {
|
||||
case forum
|
||||
case monoforum
|
||||
case botForum
|
||||
case botForum(forumManagedByUser: Bool)
|
||||
}
|
||||
|
||||
let context: AccountContext
|
||||
@@ -1249,8 +1249,13 @@ public final class ChatSideTopicsPanel: Component {
|
||||
)
|
||||
|
||||
let titleText: String
|
||||
if case .botForum = component.kind {
|
||||
titleText = component.strings.Chat_InlineTopicMenu_NewForumThreadTab
|
||||
if case let .botForum(forumManagedByUser) = component.kind {
|
||||
if forumManagedByUser {
|
||||
titleText = component.strings.Chat_InlineTopicMenu_NewForumThreadTab
|
||||
} else {
|
||||
//TODO:localize
|
||||
titleText = "All"
|
||||
}
|
||||
} else {
|
||||
titleText = component.strings.Chat_InlineTopicMenu_AllTab
|
||||
}
|
||||
@@ -1386,8 +1391,13 @@ public final class ChatSideTopicsPanel: Component {
|
||||
let rightInset: CGFloat = 12.0
|
||||
|
||||
let titleText: String
|
||||
if case .botForum = component.kind {
|
||||
titleText = component.strings.Chat_InlineTopicMenu_NewForumThreadTab
|
||||
if case let .botForum(forumManagedByUser) = component.kind {
|
||||
if forumManagedByUser {
|
||||
titleText = component.strings.Chat_InlineTopicMenu_NewForumThreadTab
|
||||
} else {
|
||||
//TODO:localize
|
||||
titleText = "All"
|
||||
}
|
||||
} else {
|
||||
titleText = component.strings.Chat_InlineTopicMenu_AllTab
|
||||
}
|
||||
@@ -2099,7 +2109,7 @@ public final class ChatSideTopicsPanel: Component {
|
||||
})
|
||||
})))
|
||||
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(ItemExtractedContentSource(
|
||||
sourceNode: sourceNode,
|
||||
@@ -2223,7 +2233,7 @@ public final class ChatSideTopicsPanel: Component {
|
||||
return
|
||||
}
|
||||
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(ItemExtractedContentSource(
|
||||
sourceNode: sourceNode,
|
||||
|
||||
+1
-1
@@ -4046,7 +4046,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
||||
}
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = PeekController(presentationData: presentationData, content: content, sourceView: {
|
||||
let controller = makePeekController(presentationData: presentationData, content: content, sourceView: {
|
||||
return (sourceView, sourceRect)
|
||||
})
|
||||
//strongSelf.peekController = controller
|
||||
|
||||
+2
-2
@@ -2336,7 +2336,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceView: sourceView, sourceRect: sourceRect)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceView: sourceView, sourceRect: sourceRect)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
strongSelf.interaction?.presentGlobalOverlayController(contextController, nil)
|
||||
})
|
||||
}
|
||||
@@ -3044,7 +3044,7 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior {
|
||||
if let forceTheme = strongSelf.forceTheme {
|
||||
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||
}
|
||||
let controller = PeekController(presentationData: presentationData, content: content, sourceView: {
|
||||
let controller = makePeekController(presentationData: presentationData, content: content, sourceView: {
|
||||
return (sourceView, sourceRect)
|
||||
})
|
||||
controller.visibilityUpdated = { [weak self] visible in
|
||||
|
||||
+1
-1
@@ -656,7 +656,7 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
|
||||
|
||||
let items = ContextController.Items(content: .list(itemList))
|
||||
|
||||
let controller = ContextController(
|
||||
let controller = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(LinkListContextExtractedContentSource(contentView: sourceView)),
|
||||
items: .single(items),
|
||||
|
||||
+16
-6
@@ -725,8 +725,8 @@ public final class ChatListHeaderComponent: Component {
|
||||
|
||||
private let leftButtonsContainer: UIView
|
||||
private let rightButtonsContainer: UIView
|
||||
private var leftButtonsBackgroundContainer: GlassBackgroundView?
|
||||
private var rightButtonsBackgroundContainer: GlassBackgroundView?
|
||||
private var leftButtonsBackgroundContainer: GlassContextExtractableContainer?
|
||||
private var rightButtonsBackgroundContainer: GlassContextExtractableContainer?
|
||||
|
||||
private let storyPeerListExternalState = StoryPeerListComponent.ExternalState()
|
||||
private var storyPeerList: ComponentView<Empty>?
|
||||
@@ -774,6 +774,16 @@ public final class ChatListHeaderComponent: Component {
|
||||
return self.storyPeerList?.view as? StoryPeerListComponent.View
|
||||
}
|
||||
|
||||
public func navigationButtonContextContainer(sourceView: UIView) -> ContextExtractableContainer? {
|
||||
if let leftButtonsBackgroundContainer = self.leftButtonsBackgroundContainer, sourceView.isDescendant(of: leftButtonsBackgroundContainer) {
|
||||
return leftButtonsBackgroundContainer
|
||||
}
|
||||
if let rightButtonsBackgroundContainer = self.rightButtonsBackgroundContainer, sourceView.isDescendant(of: rightButtonsBackgroundContainer) {
|
||||
return rightButtonsBackgroundContainer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if let storyPeerListView = self.storyPeerList?.view {
|
||||
if let result = storyPeerListView.hitTest(self.convert(point, to: storyPeerListView), with: event) {
|
||||
@@ -1094,13 +1104,13 @@ public final class ChatListHeaderComponent: Component {
|
||||
}
|
||||
|
||||
if leftButtonsEffectiveWidth != 0.0 {
|
||||
let leftButtonsBackgroundContainer: GlassBackgroundView
|
||||
let leftButtonsBackgroundContainer: GlassContextExtractableContainer
|
||||
var leftButtonsBackgroundContainerTransition = transition
|
||||
if let current = self.leftButtonsBackgroundContainer {
|
||||
leftButtonsBackgroundContainer = current
|
||||
} else {
|
||||
leftButtonsBackgroundContainerTransition = leftButtonsBackgroundContainerTransition.withAnimation(.none)
|
||||
leftButtonsBackgroundContainer = GlassBackgroundView()
|
||||
leftButtonsBackgroundContainer = GlassContextExtractableContainer()
|
||||
self.leftButtonsBackgroundContainer = leftButtonsBackgroundContainer
|
||||
self.addSubview(leftButtonsBackgroundContainer)
|
||||
leftButtonsBackgroundContainer.contentView.addSubview(self.leftButtonsContainer)
|
||||
@@ -1119,7 +1129,7 @@ public final class ChatListHeaderComponent: Component {
|
||||
}
|
||||
|
||||
if rightButtonsEffectiveWidth != 0.0 {
|
||||
let rightButtonsBackgroundContainer: GlassBackgroundView
|
||||
let rightButtonsBackgroundContainer: GlassContextExtractableContainer
|
||||
var rightButtonsBackgroundContainerTransition = transition
|
||||
|
||||
let rightButtonsContainerFrame = CGRect(origin: CGPoint(x: availableSize.width - component.sideInset - max(44.0, rightButtonsEffectiveWidth), y: 0.0), size: CGSize(width: max(44.0, rightButtonsEffectiveWidth), height: 44.0))
|
||||
@@ -1128,7 +1138,7 @@ public final class ChatListHeaderComponent: Component {
|
||||
rightButtonsBackgroundContainer = current
|
||||
} else {
|
||||
rightButtonsBackgroundContainerTransition = rightButtonsBackgroundContainerTransition.withAnimation(.none)
|
||||
rightButtonsBackgroundContainer = GlassBackgroundView()
|
||||
rightButtonsBackgroundContainer = GlassContextExtractableContainer()
|
||||
self.rightButtonsBackgroundContainer = rightButtonsBackgroundContainer
|
||||
self.addSubview(rightButtonsBackgroundContainer)
|
||||
rightButtonsBackgroundContainer.contentView.addSubview(self.rightButtonsContainer)
|
||||
|
||||
@@ -115,17 +115,7 @@ public final class ChatNavigationBarTitleView: UIView, NavigationBarTitleView {
|
||||
let transition = ComponentTransition(transition)
|
||||
|
||||
if let contentData = self.contentData {
|
||||
let displayBackground: Bool
|
||||
if let singleColor = contentData.wallpaper.singleColor {
|
||||
let brightness = singleColor.brightness
|
||||
if brightness <= 0.2 || brightness >= 0.8 {
|
||||
displayBackground = false
|
||||
} else {
|
||||
displayBackground = true
|
||||
}
|
||||
} else {
|
||||
displayBackground = true
|
||||
}
|
||||
let displayBackground: Bool = true
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
@@ -1039,6 +1029,7 @@ public final class ChatTitleComponent: Component {
|
||||
if let minSubtitleWidth {
|
||||
contentSize.width = max(contentSize.width, minSubtitleWidth)
|
||||
}
|
||||
contentSize.width = max(min(150.0, availableSize.width - containerSideInset * 2.0), contentSize.width)
|
||||
contentSize.height += subtitleSize.height
|
||||
|
||||
let containerSize = CGSize(width: contentSize.width + containerSideInset * 2.0, height: 44.0)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ContextControllerImpl",
|
||||
module_name = "ContextControllerImpl",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/TelegramUI/Components/AnimationCache",
|
||||
"//submodules/AnimationUI",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/Display",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
"//submodules/TelegramUI/Components/EntityKeyboard",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/Markdown",
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
|
||||
"//submodules/ReactionSelectionNode",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/TelegramUI/Components/TextNodeWithEntities",
|
||||
"//submodules/TextSelectionNode",
|
||||
"//submodules/UIKitRuntimeUtils",
|
||||
"//submodules/UndoUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
+1
-13
@@ -5,19 +5,7 @@ import Display
|
||||
import TelegramPresentationData
|
||||
import SwiftSignalKit
|
||||
import Markdown
|
||||
|
||||
public enum ContextActionSibling {
|
||||
case none
|
||||
case item
|
||||
case separator
|
||||
}
|
||||
|
||||
public protocol ContextActionNodeProtocol: ASDisplayNode {
|
||||
func setIsHighlighted(_ value: Bool)
|
||||
func performAction()
|
||||
func actionNode(at point: CGPoint) -> ContextActionNodeProtocol
|
||||
var isActionEnabled: Bool { get }
|
||||
}
|
||||
import ContextUI
|
||||
|
||||
public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
||||
private var presentationData: PresentationData
|
||||
+1
@@ -10,6 +10,7 @@ import AppBundle
|
||||
import TextFormat
|
||||
import TextNodeWithEntities
|
||||
import SwiftSignalKit
|
||||
import ContextUI
|
||||
|
||||
private final class ContextActionsSelectionGestureRecognizer: UIPanGestureRecognizer {
|
||||
var updateLocation: ((CGPoint, Bool) -> Void)?
|
||||
+66
-79
@@ -18,76 +18,7 @@ import ComponentDisplayAdapters
|
||||
import GlassBackgroundComponent
|
||||
import LottieComponent
|
||||
import TextNodeWithEntities
|
||||
|
||||
public protocol ContextControllerActionsStackItemNode: ASDisplayNode {
|
||||
var wantsFullWidth: Bool { get }
|
||||
|
||||
func update(
|
||||
presentationData: PresentationData,
|
||||
constrainedSize: CGSize,
|
||||
standardMinWidth: CGFloat,
|
||||
standardMaxWidth: CGFloat,
|
||||
additionalBottomInset: CGFloat,
|
||||
transition: ContainedViewLayoutTransition
|
||||
) -> (size: CGSize, apparentHeight: CGFloat)
|
||||
|
||||
func highlightGestureMoved(location: CGPoint)
|
||||
func highlightGestureFinished(performAction: Bool)
|
||||
|
||||
func decreaseHighlightedIndex()
|
||||
func increaseHighlightedIndex()
|
||||
}
|
||||
|
||||
public struct ContextControllerReactionItems {
|
||||
public var context: AccountContext
|
||||
public var reactionItems: [ReactionContextItem]
|
||||
public var selectedReactionItems: Set<MessageReaction.Reaction>
|
||||
public var reactionsTitle: String?
|
||||
public var reactionsLocked: Bool
|
||||
public var animationCache: AnimationCache
|
||||
public var alwaysAllowPremiumReactions: Bool
|
||||
public var allPresetReactionsAreAvailable: Bool
|
||||
public var getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal<EmojiPagerContentComponent, NoError>)?
|
||||
|
||||
public init(context: AccountContext, reactionItems: [ReactionContextItem], selectedReactionItems: Set<MessageReaction.Reaction>, reactionsTitle: String?, reactionsLocked: Bool, animationCache: AnimationCache, alwaysAllowPremiumReactions: Bool, allPresetReactionsAreAvailable: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal<EmojiPagerContentComponent, NoError>)?) {
|
||||
self.context = context
|
||||
self.reactionItems = reactionItems
|
||||
self.selectedReactionItems = selectedReactionItems
|
||||
self.reactionsTitle = reactionsTitle
|
||||
self.reactionsLocked = reactionsLocked
|
||||
self.animationCache = animationCache
|
||||
self.alwaysAllowPremiumReactions = alwaysAllowPremiumReactions
|
||||
self.allPresetReactionsAreAvailable = allPresetReactionsAreAvailable
|
||||
self.getEmojiContent = getEmojiContent
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextControllerPreviewReaction {
|
||||
public let context: AccountContext
|
||||
public let file: TelegramMediaFile
|
||||
|
||||
public init(context: AccountContext, file: TelegramMediaFile) {
|
||||
self.context = context
|
||||
self.file = file
|
||||
}
|
||||
}
|
||||
|
||||
public protocol ContextControllerActionsStackItem: AnyObject {
|
||||
func node(
|
||||
context: AccountContext?,
|
||||
getController: @escaping () -> ContextControllerProtocol?,
|
||||
requestDismiss: @escaping (ContextMenuActionResult) -> Void,
|
||||
requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void,
|
||||
requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void
|
||||
) -> ContextControllerActionsStackItemNode
|
||||
|
||||
var id: AnyHashable? { get }
|
||||
var tip: ContextController.Tip? { get }
|
||||
var tipSignal: Signal<ContextController.Tip?, NoError>? { get }
|
||||
var reactionItems: ContextControllerReactionItems? { get }
|
||||
var previewReaction: ContextControllerPreviewReaction? { get }
|
||||
var dismissed: (() -> Void)? { get }
|
||||
}
|
||||
import ContextUI
|
||||
|
||||
public protocol ContextControllerActionsListItemNode: ASDisplayNode {
|
||||
func update(presentationData: PresentationData, constrainedSize: CGSize) -> (minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void)
|
||||
@@ -1326,17 +1257,11 @@ private final class ItemSelectionRecognizer: UIGestureRecognizer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
public enum Presentation {
|
||||
case modal
|
||||
case inline
|
||||
case additional
|
||||
}
|
||||
|
||||
public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, ContextControllerActionsStackNode {
|
||||
final class NavigationContainer: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
let backgroundContainer: GlassBackgroundContainerView
|
||||
let backgroundView: GlassBackgroundView
|
||||
var sourceExtractableContainer: ContextExtractableContainer?
|
||||
let contentContainer: UIView
|
||||
|
||||
var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
|
||||
@@ -1417,6 +1342,55 @@ public final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func animateIn(fromExtractableContainer extractableContainer: ContextExtractableContainer) {
|
||||
let transition: ComponentTransition = .spring(duration: 0.42)
|
||||
|
||||
let normalSize = extractableContainer.extractableContentView.bounds.size
|
||||
let normalCornerRadius: CGFloat = min(normalSize.width, normalSize.height) * 0.5
|
||||
|
||||
self.sourceExtractableContainer = extractableContainer
|
||||
self.backgroundView.isHidden = true
|
||||
|
||||
self.backgroundContainer.contentView.addSubview(extractableContainer.extractableContentView)
|
||||
for subview in extractableContainer.extractableContentView.subviews {
|
||||
if let subview = subview as? GlassBackgroundView {
|
||||
//TODO:release
|
||||
subview.contentView.addSubview(self.contentContainer)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.sourceExtractableContainer = nil
|
||||
self.contentContainer.frame = CGRect(origin: CGPoint(), size: normalSize)
|
||||
self.contentContainer.layer.cornerRadius = normalCornerRadius
|
||||
|
||||
transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: self.backgroundContainer.bounds.size))
|
||||
transition.setCornerRadius(layer: self.contentContainer.layer, cornerRadius: 30.0)
|
||||
self.contentContainer.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
|
||||
extractableContainer.updateState(state: .extracted(size: normalSize, cornerRadius: normalCornerRadius, state: .animatedOut), transition: .immediate)
|
||||
extractableContainer.updateState(state: .extracted(size: self.backgroundContainer.bounds.size, cornerRadius: 30.0, state: .animatedIn), transition: transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
func animateOut(toExtractableContainer extractableContainer: ContextExtractableContainer, transition: ContainedViewLayoutTransition) {
|
||||
let transition = ComponentTransition(transition)
|
||||
|
||||
let normalSize = extractableContainer.extractableContentView.bounds.size
|
||||
let normalCornerRadius: CGFloat = min(normalSize.width, normalSize.height) * 0.5
|
||||
|
||||
transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: normalSize))
|
||||
transition.attachAnimation(view: self.contentContainer, id: "animateOut", completion: { [weak extractableContainer] _ in
|
||||
guard let extractableContainer else {
|
||||
return
|
||||
}
|
||||
extractableContainer.addSubview(extractableContainer.extractableContentView)
|
||||
})
|
||||
transition.setCornerRadius(layer: self.contentContainer.layer, cornerRadius: normalCornerRadius)
|
||||
transition.setAlpha(view: self.contentContainer, alpha: 0.0)
|
||||
|
||||
extractableContainer.updateState(state: .normal, transition: transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
func update(presentationData: PresentationData, presentation: Presentation, size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
let transition = ComponentTransition(transition)
|
||||
|
||||
@@ -1443,7 +1417,12 @@ public final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.backgroundView.update(size: size, cornerRadius: 30.0, isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: false, transition: transition)
|
||||
self.backgroundView.update(size: size, cornerRadius: 30.0, isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
|
||||
|
||||
if let sourceExtractableContainer = self.sourceExtractableContainer {
|
||||
transition.setFrame(view: sourceExtractableContainer.extractableContentView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
sourceExtractableContainer.updateState(state: .extracted(size: size, cornerRadius: 30.0, state: .animatedIn), transition: transition.containedViewLayoutTransition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2080,4 +2059,12 @@ public final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func animateIn(fromExtractableContainer extractableContainer: ContextExtractableContainer) {
|
||||
self.navigationContainer.animateIn(fromExtractableContainer: extractableContainer)
|
||||
}
|
||||
|
||||
func animateOut(toExtractableContainer extractableContainer: ContextExtractableContainer, transition: ContainedViewLayoutTransition) {
|
||||
self.navigationContainer.animateOut(toExtractableContainer: extractableContainer, transition: transition)
|
||||
}
|
||||
}
|
||||
+118
-82
@@ -9,6 +9,9 @@ import SwiftSignalKit
|
||||
import ReactionSelectionNode
|
||||
import UndoUI
|
||||
import AccountContext
|
||||
import ContextUI
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
|
||||
private extension ContextControllerTakeViewInfo.ContainingItem {
|
||||
var contentRect: CGRect {
|
||||
@@ -264,7 +267,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
private let contentRectDebugNode: ASDisplayNode
|
||||
|
||||
private var actionsContainerNode: ASDisplayNode
|
||||
private let actionsStackNode: ContextControllerActionsStackNode
|
||||
private let actionsStackNode: ContextControllerActionsStackNodeImpl
|
||||
private let additionalActionsStackNode: ContextControllerActionsStackNode
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
@@ -324,7 +327,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
self.contentRectDebugNode.backgroundColor = UIColor.red.withAlphaComponent(0.2)
|
||||
|
||||
self.actionsContainerNode = ASDisplayNode()
|
||||
self.actionsStackNode = ContextControllerActionsStackNode(
|
||||
self.actionsStackNode = ContextControllerActionsStackNodeImpl(
|
||||
context: self.context,
|
||||
getController: getController,
|
||||
requestDismiss: { result in
|
||||
@@ -333,7 +336,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
requestUpdate: requestUpdate
|
||||
)
|
||||
|
||||
self.additionalActionsStackNode = ContextControllerActionsStackNode(
|
||||
self.additionalActionsStackNode = ContextControllerActionsStackNodeImpl(
|
||||
context: self.context,
|
||||
getController: getController,
|
||||
requestDismiss: { result in
|
||||
@@ -662,7 +665,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
var contentTopInset: CGFloat = topInset
|
||||
var removedReactionContextNode: ReactionContextNode?
|
||||
|
||||
if let reactionItems = self.actionsStackNode.topReactionItems, !reactionItems.reactionItems.isEmpty, let controller = self.getController() as? ContextController {
|
||||
if let reactionItems = self.actionsStackNode.topReactionItems, !reactionItems.reactionItems.isEmpty, let controller = self.getController() as? ContextControllerImpl {
|
||||
let reactionContextNode: ReactionContextNode
|
||||
if let current = self.reactionContextNode {
|
||||
reactionContextNode = current
|
||||
@@ -707,14 +710,14 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
|
||||
reactionContextNode.reactionSelected = { [weak self] reaction, isLarge in
|
||||
guard let strongSelf = self, let controller = strongSelf.getController() as? ContextController else {
|
||||
guard let strongSelf = self, let controller = strongSelf.getController() as? ContextControllerImpl else {
|
||||
return
|
||||
}
|
||||
controller.reactionSelected?(reaction, isLarge)
|
||||
}
|
||||
let context = reactionItems.context
|
||||
reactionContextNode.premiumReactionsSelected = { [weak self] file in
|
||||
guard let strongSelf = self, let validLayout = strongSelf.validLayout, let controller = strongSelf.getController() as? ContextController else {
|
||||
guard let strongSelf = self, let validLayout = strongSelf.validLayout, let controller = strongSelf.getController() as? ContextControllerImpl else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -799,6 +802,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
var contentRect: CGRect
|
||||
var isContentResizeableVertically: Bool = false
|
||||
let _ = isContentResizeableVertically
|
||||
var contextExtractableContainer: (container: ContextExtractableContainer, sourceRect: CGRect)?
|
||||
|
||||
switch self.source {
|
||||
case let .location(location):
|
||||
@@ -810,6 +814,10 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
case let .reference(reference):
|
||||
if let transitionInfo = reference.transitionInfo() {
|
||||
if let referenceView = transitionInfo.referenceView as? ContextExtractableContainer {
|
||||
contextExtractableContainer = (referenceView, convertFrame(transitionInfo.referenceView.bounds.inset(by: transitionInfo.insets), from: transitionInfo.referenceView, to: self.view))
|
||||
}
|
||||
|
||||
contentRect = convertFrame(transitionInfo.referenceView.bounds.inset(by: transitionInfo.insets), from: transitionInfo.referenceView, to: self.view).insetBy(dx: -2.0, dy: 0.0)
|
||||
contentRect.size.width += 5.0
|
||||
contentParentGlobalFrame = CGRect(origin: CGPoint(x: 0.0, y: contentRect.minY), size: CGSize(width: layout.size.width, height: contentRect.height))
|
||||
@@ -1051,14 +1059,17 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
transition.updateFrame(node: self.contentRectDebugNode, frame: contentRect, beginWithCurrentState: true)
|
||||
|
||||
var actionsFrame: CGRect
|
||||
if case let .reference(source) = self.source, let actionsPosition = source.transitionInfo()?.actionsPosition, case .top = actionsPosition {
|
||||
if let contextExtractableContainer {
|
||||
let _ = contextExtractableContainer
|
||||
actionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: contentRect.minY), size: actionsSize)
|
||||
} else if case let .reference(source) = self.source, let actionsPosition = source.transitionInfo()?.actionsPosition, case .top = actionsPosition {
|
||||
actionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: contentRect.minY - contentActionsSpacing - actionsSize.height), size: actionsSize)
|
||||
} else {
|
||||
actionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: contentRect.maxY + contentActionsSpacing), size: actionsSize)
|
||||
}
|
||||
var contentVerticalOffset: CGFloat = 0.0
|
||||
|
||||
if keepInPlace, case .extracted = self.source {
|
||||
if contextExtractableContainer == nil, keepInPlace, case .extracted = self.source {
|
||||
actionsFrame.origin.y = contentRect.minY - contentActionsSpacing - actionsFrame.height
|
||||
let statusBarHeight = (layout.statusBarHeight ?? 0.0)
|
||||
if actionsFrame.origin.y < statusBarHeight {
|
||||
@@ -1333,52 +1344,65 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
currentContentScreenFrame = contentRect
|
||||
}
|
||||
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: self.actionsContainerNode.alpha, duration: 0.05)
|
||||
self.actionsContainerNode.layer.animateSpring(
|
||||
from: 0.01 as NSNumber,
|
||||
to: 1.0 as NSNumber,
|
||||
keyPath: "transform.scale",
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
initialVelocity: 0.0,
|
||||
damping: springDamping,
|
||||
additive: false
|
||||
)
|
||||
|
||||
var actionsPositionDeltaXDistance: CGFloat = 0.0
|
||||
if case .center = actionsHorizontalAlignment {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsContainerNode.frame.midX
|
||||
}
|
||||
|
||||
if case .reference = self.source {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsContainerNode.frame.midX
|
||||
}
|
||||
|
||||
let actionsVerticalTransitionDirection: CGFloat
|
||||
if let contentNode = itemContentNode {
|
||||
if contentNode.frame.minY < self.actionsContainerNode.frame.minY {
|
||||
actionsVerticalTransitionDirection = -1.0
|
||||
} else {
|
||||
actionsVerticalTransitionDirection = 1.0
|
||||
}
|
||||
if let contextExtractableContainer {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: .spring)
|
||||
|
||||
transition.animatePositionAdditive(layer: self.actionsContainerNode.layer, offset: CGPoint(
|
||||
x: contextExtractableContainer.sourceRect.minX - self.actionsContainerNode.frame.minX,
|
||||
y: contextExtractableContainer.sourceRect.minY - self.actionsContainerNode.frame.minY
|
||||
))
|
||||
|
||||
self.actionsStackNode.animateIn(fromExtractableContainer: contextExtractableContainer.container)
|
||||
} else {
|
||||
if contentRect.minY < self.actionsContainerNode.frame.minY {
|
||||
actionsVerticalTransitionDirection = -1.0
|
||||
} else {
|
||||
actionsVerticalTransitionDirection = 1.0
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: self.actionsContainerNode.alpha, duration: 0.05)
|
||||
self.actionsContainerNode.layer.animateSpring(
|
||||
from: 0.01 as NSNumber,
|
||||
to: 1.0 as NSNumber,
|
||||
keyPath: "transform.scale",
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
initialVelocity: 0.0,
|
||||
damping: springDamping,
|
||||
additive: false
|
||||
)
|
||||
|
||||
var actionsPositionDeltaXDistance: CGFloat = 0.0
|
||||
if case .center = actionsHorizontalAlignment {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsContainerNode.frame.midX
|
||||
}
|
||||
|
||||
if case .reference = self.source {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsContainerNode.frame.midX
|
||||
}
|
||||
|
||||
let actionsVerticalTransitionDirection: CGFloat
|
||||
if let contentNode = itemContentNode {
|
||||
if contentNode.frame.minY < self.actionsContainerNode.frame.minY {
|
||||
actionsVerticalTransitionDirection = -1.0
|
||||
} else {
|
||||
actionsVerticalTransitionDirection = 1.0
|
||||
}
|
||||
} else {
|
||||
if contentRect.minY < self.actionsContainerNode.frame.minY {
|
||||
actionsVerticalTransitionDirection = -1.0
|
||||
} else {
|
||||
actionsVerticalTransitionDirection = 1.0
|
||||
}
|
||||
}
|
||||
let actionsPositionDeltaYDistance = -animationInContentYDistance + actionsVerticalTransitionDirection * actionsSize.height / 2.0 - contentActionsSpacing
|
||||
self.actionsContainerNode.layer.animateSpring(
|
||||
from: NSValue(cgPoint: CGPoint(x: actionsPositionDeltaXDistance, y: actionsPositionDeltaYDistance)),
|
||||
to: NSValue(cgPoint: CGPoint()),
|
||||
keyPath: "position",
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
initialVelocity: 0.0,
|
||||
damping: springDamping,
|
||||
additive: true
|
||||
)
|
||||
|
||||
self.actionsStackNode.animateIn()
|
||||
}
|
||||
let actionsPositionDeltaYDistance = -animationInContentYDistance + actionsVerticalTransitionDirection * actionsSize.height / 2.0 - contentActionsSpacing
|
||||
self.actionsContainerNode.layer.animateSpring(
|
||||
from: NSValue(cgPoint: CGPoint(x: actionsPositionDeltaXDistance, y: actionsPositionDeltaYDistance)),
|
||||
to: NSValue(cgPoint: CGPoint()),
|
||||
keyPath: "position",
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
initialVelocity: 0.0,
|
||||
damping: springDamping,
|
||||
additive: true
|
||||
)
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
let reactionsPositionDeltaYDistance = -animationInContentYDistance
|
||||
@@ -1395,8 +1419,6 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
reactionContextNode.animateIn(from: currentContentScreenFrame)
|
||||
}
|
||||
|
||||
self.actionsStackNode.animateIn()
|
||||
|
||||
if let contentNode = itemContentNode {
|
||||
contentNode.containingItem.isExtractedToContextPreview = true
|
||||
contentNode.containingItem.isExtractedToContextPreviewUpdated?(true)
|
||||
@@ -1678,41 +1700,55 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
)
|
||||
}
|
||||
|
||||
self.actionsContainerNode.layer.animateAlpha(from: self.actionsContainerNode.alpha, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
self.actionsContainerNode.layer.animate(
|
||||
from: 1.0 as NSNumber,
|
||||
to: 0.01 as NSNumber,
|
||||
keyPath: "transform.scale",
|
||||
timingFunction: timingFunction,
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
removeOnCompletion: false,
|
||||
completion: { _ in
|
||||
if let contextExtractableContainer {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: .easeInOut)
|
||||
|
||||
transition.updateFrame(node: self.actionsContainerNode, frame: CGRect(origin: CGPoint(x: contextExtractableContainer.sourceRect.minX, y: contextExtractableContainer.sourceRect.minY), size: self.actionsContainerNode.bounds.size))
|
||||
ComponentTransition(transition).attachAnimation(view: self.actionsContainerNode.view, id: "animateOut", completion: { _ in
|
||||
if completeWithActionStack {
|
||||
restoreOverlayViews.forEach({ $0() })
|
||||
completion()
|
||||
}
|
||||
})
|
||||
|
||||
self.actionsStackNode.animateOut(toExtractableContainer: contextExtractableContainer.container, transition: transition)
|
||||
} else {
|
||||
self.actionsContainerNode.layer.animateAlpha(from: self.actionsContainerNode.alpha, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
self.actionsContainerNode.layer.animate(
|
||||
from: 1.0 as NSNumber,
|
||||
to: 0.01 as NSNumber,
|
||||
keyPath: "transform.scale",
|
||||
timingFunction: timingFunction,
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
removeOnCompletion: false,
|
||||
completion: { _ in
|
||||
if completeWithActionStack {
|
||||
restoreOverlayViews.forEach({ $0() })
|
||||
completion()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
var actionsPositionDeltaXDistance: CGFloat = 0.0
|
||||
if case .center = actionsHorizontalAlignment {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsContainerNode.frame.midX
|
||||
}
|
||||
)
|
||||
|
||||
var actionsPositionDeltaXDistance: CGFloat = 0.0
|
||||
if case .center = actionsHorizontalAlignment {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsContainerNode.frame.midX
|
||||
if case .reference = self.source {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsContainerNode.frame.midX
|
||||
}
|
||||
let actionsPositionDeltaYDistance = -animationInContentYDistance + actionsVerticalTransitionDirection * actionsSize.height / 2.0 - contentActionsSpacing
|
||||
self.actionsContainerNode.layer.animate(
|
||||
from: NSValue(cgPoint: CGPoint()),
|
||||
to: NSValue(cgPoint: CGPoint(x: actionsPositionDeltaXDistance, y: actionsPositionDeltaYDistance)),
|
||||
keyPath: "position",
|
||||
timingFunction: timingFunction,
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
removeOnCompletion: false,
|
||||
additive: true
|
||||
)
|
||||
}
|
||||
if case .reference = self.source {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsContainerNode.frame.midX
|
||||
}
|
||||
let actionsPositionDeltaYDistance = -animationInContentYDistance + actionsVerticalTransitionDirection * actionsSize.height / 2.0 - contentActionsSpacing
|
||||
self.actionsContainerNode.layer.animate(
|
||||
from: NSValue(cgPoint: CGPoint()),
|
||||
to: NSValue(cgPoint: CGPoint(x: actionsPositionDeltaXDistance, y: actionsPositionDeltaYDistance)),
|
||||
keyPath: "position",
|
||||
timingFunction: timingFunction,
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
removeOnCompletion: false,
|
||||
additive: true
|
||||
)
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.animateOut(to: currentContentScreenFrame, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
||||
+2203
File diff suppressed because it is too large
Load Diff
+1
@@ -7,6 +7,7 @@ import TextSelectionNode
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import ReactionSelectionNode
|
||||
import ContextUI
|
||||
|
||||
enum ContextControllerPresentationNodeStateTransition {
|
||||
case animateIn
|
||||
+5
-4
@@ -12,10 +12,11 @@ import PlainButtonComponent
|
||||
import MultilineTextComponent
|
||||
import ComponentDisplayAdapters
|
||||
import AccountContext
|
||||
import ContextUI
|
||||
|
||||
final class ContextSourceContainer: ASDisplayNode {
|
||||
final class Source {
|
||||
weak var controller: ContextController?
|
||||
weak var controller: ContextControllerImpl?
|
||||
|
||||
let id: AnyHashable
|
||||
let title: String
|
||||
@@ -44,7 +45,7 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
private let actionsReady = Promise<Bool>()
|
||||
|
||||
init(
|
||||
controller: ContextController,
|
||||
controller: ContextControllerImpl,
|
||||
id: AnyHashable,
|
||||
title: String,
|
||||
footer: String?,
|
||||
@@ -359,7 +360,7 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private weak var controller: ContextController?
|
||||
private weak var controller: ContextControllerImpl?
|
||||
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
|
||||
@@ -387,7 +388,7 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
return self.activeSource?.presentationNode.wantsDisplayBelowKeyboard() ?? false
|
||||
}
|
||||
|
||||
init(controller: ContextController, configuration: ContextController.Configuration, context: AccountContext?) {
|
||||
init(controller: ContextControllerImpl, configuration: ContextController.Configuration, context: AccountContext?) {
|
||||
self.controller = controller
|
||||
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: false)
|
||||
@@ -0,0 +1,120 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import ContextUI
|
||||
|
||||
public final class PeekControllerImpl: ViewController, PeekController, ContextControllerProtocol {
|
||||
public var useComplexItemsTransitionAnimation: Bool = false
|
||||
public var immediateItemsTransitionAnimation = false
|
||||
|
||||
public func getActionsMinHeight() -> ContextController.ActionsHeight? {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, animated: Bool) {
|
||||
}
|
||||
|
||||
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, previousActionsTransition: ContextController.PreviousActionsTransition) {
|
||||
}
|
||||
|
||||
public func pushItems(items: Signal<ContextController.Items, NoError>) {
|
||||
self.controllerNode.pushItems(items: items)
|
||||
}
|
||||
|
||||
public func popItems() {
|
||||
self.controllerNode.popItems()
|
||||
}
|
||||
|
||||
private var controllerNode: PeekControllerNode {
|
||||
return self.displayNode as! PeekControllerNode
|
||||
}
|
||||
|
||||
public var contentNode: PeekControllerContentNode & ASDisplayNode {
|
||||
return self.controllerNode.contentNode
|
||||
}
|
||||
|
||||
private let presentationData: PresentationData
|
||||
private let content: PeekControllerContent
|
||||
public var sourceView: () -> (UIView, CGRect)?
|
||||
private let activateImmediately: Bool
|
||||
|
||||
public var visibilityUpdated: ((Bool) -> Void)?
|
||||
|
||||
public var getOverlayViews: (() -> [UIView])?
|
||||
|
||||
public var appeared: (() -> Void)?
|
||||
public var disappeared: (() -> Void)?
|
||||
|
||||
private var animatedIn = false
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
override public var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
public init(presentationData: PresentationData, content: PeekControllerContent, sourceView: @escaping () -> (UIView, CGRect)?, activateImmediately: Bool = false) {
|
||||
self.presentationData = presentationData
|
||||
self.content = content
|
||||
self.sourceView = sourceView
|
||||
self.activateImmediately = activateImmediately
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = PeekControllerNode(presentationData: self.presentationData, controller: self, content: self.content, requestDismiss: { [weak self] in
|
||||
self?.dismiss()
|
||||
})
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
private func getSourceRect() -> CGRect {
|
||||
if let (sourceView, sourceRect) = self.sourceView() {
|
||||
return sourceView.convert(sourceRect, to: self.view)
|
||||
} else {
|
||||
let size = self.displayNode.bounds.size
|
||||
return CGRect(origin: CGPoint(x: floor((size.width - 10.0) / 2.0), y: floor((size.height - 10.0) / 2.0)), size: CGSize(width: 10.0, height: 10.0))
|
||||
}
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if !self.animatedIn {
|
||||
self.animatedIn = true
|
||||
self.controllerNode.animateIn(from: self.getSourceRect())
|
||||
|
||||
self.visibilityUpdated?(true)
|
||||
|
||||
if self.activateImmediately {
|
||||
self.controllerNode.activateMenu(immediately: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.visibilityUpdated?(false)
|
||||
self.controllerNode.animateOut(to: self.getSourceRect(), completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
})
|
||||
}
|
||||
|
||||
public func dismiss(result: ContextMenuActionResult, completion: (() -> Void)?) {
|
||||
self.dismiss(completion: completion)
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -4,10 +4,11 @@ import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import ContextUI
|
||||
|
||||
private let animationDurationFactor: Double = 1.0
|
||||
|
||||
final class PeekControllerNode: ViewControllerTracingNode {
|
||||
final class PeekControllerNode: ViewControllerTracingNode, PeekControllerNodeProtocol {
|
||||
private let requestDismiss: () -> Void
|
||||
|
||||
private let presentationData: PresentationData
|
||||
@@ -79,7 +80,7 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
var activatedActionImpl: (() -> Void)?
|
||||
var requestLayoutImpl: ((ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
self.actionsStackNode = ContextControllerActionsStackNode(
|
||||
self.actionsStackNode = ContextControllerActionsStackNodeImpl(
|
||||
context: nil,
|
||||
getController: { [weak controller] in
|
||||
return controller
|
||||
@@ -405,7 +406,7 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
}
|
||||
|
||||
func activateMenu(immediately: Bool = false) {
|
||||
func activateMenu(immediately: Bool) {
|
||||
if self.content.menuItems().isEmpty {
|
||||
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode {
|
||||
fullScreenAccessoryNode.alpha = 1.0
|
||||
@@ -0,0 +1,262 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import TextSelectionNode
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import UIKitRuntimeUtils
|
||||
import ContextUI
|
||||
|
||||
private final class PinchControllerNode: ViewControllerTracingNode {
|
||||
private weak var controller: PinchController?
|
||||
|
||||
private var initialSourceFrame: CGRect?
|
||||
|
||||
private let clippingNode: ASDisplayNode
|
||||
private let scrollingContainer: ASDisplayNode
|
||||
|
||||
private let sourceNode: PinchSourceContainerNode
|
||||
private let disableScreenshots: Bool
|
||||
private let getContentAreaInScreenSpace: () -> CGRect
|
||||
|
||||
private let dimNode: ASDisplayNode
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var isAnimatingOut: Bool = false
|
||||
|
||||
private var hapticFeedback: HapticFeedback?
|
||||
|
||||
init(controller: PinchController, sourceNode: PinchSourceContainerNode, disableScreenshots: Bool, getContentAreaInScreenSpace: @escaping () -> CGRect) {
|
||||
self.controller = controller
|
||||
self.sourceNode = sourceNode
|
||||
self.disableScreenshots = disableScreenshots
|
||||
self.getContentAreaInScreenSpace = getContentAreaInScreenSpace
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
||||
self.dimNode.alpha = 0.0
|
||||
|
||||
self.clippingNode = ASDisplayNode()
|
||||
self.clippingNode.clipsToBounds = true
|
||||
|
||||
self.scrollingContainer = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.clippingNode)
|
||||
self.clippingNode.addSubnode(self.scrollingContainer)
|
||||
|
||||
self.sourceNode.deactivate = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.controller?.dismiss()
|
||||
}
|
||||
|
||||
self.sourceNode.updated = { [weak self] scale, pinchLocation, offset in
|
||||
guard let strongSelf = self, let initialSourceFrame = strongSelf.initialSourceFrame else {
|
||||
return
|
||||
}
|
||||
strongSelf.dimNode.alpha = max(0.0, min(1.0, scale - 1.0))
|
||||
|
||||
let pinchOffset = CGPoint(
|
||||
x: pinchLocation.x - initialSourceFrame.width / 2.0,
|
||||
y: pinchLocation.y - initialSourceFrame.height / 2.0
|
||||
)
|
||||
|
||||
var transform = CATransform3DIdentity
|
||||
transform = CATransform3DTranslate(transform, offset.x - pinchOffset.x * (scale - 1.0), offset.y - pinchOffset.y * (scale - 1.0), 0.0)
|
||||
transform = CATransform3DScale(transform, scale, scale, 0.0)
|
||||
|
||||
strongSelf.sourceNode.contentNode.transform = transform
|
||||
}
|
||||
|
||||
if self.disableScreenshots {
|
||||
setLayerDisableScreenshots(self.layer, true)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
}
|
||||
|
||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?) {
|
||||
if self.isAnimatingOut {
|
||||
return
|
||||
}
|
||||
|
||||
self.validLayout = layout
|
||||
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
let convertedFrame = convertFrame(self.sourceNode.bounds, from: self.sourceNode.view, to: self.view)
|
||||
self.sourceNode.contentNode.frame = convertedFrame
|
||||
self.initialSourceFrame = convertedFrame
|
||||
self.scrollingContainer.addSubnode(self.sourceNode.contentNode)
|
||||
|
||||
var updatedContentAreaInScreenSpace = self.getContentAreaInScreenSpace()
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
|
||||
self.clippingNode.layer.animateFrame(from: updatedContentAreaInScreenSpace, to: self.clippingNode.frame, duration: 0.18 * 1.0, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: updatedContentAreaInScreenSpace.minY, to: 0.0, duration: 0.18 * 1.0, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
}
|
||||
|
||||
func animateOut(completion: @escaping () -> Void) {
|
||||
self.isAnimatingOut = true
|
||||
|
||||
let performCompletion: () -> Void = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.isAnimatingOut = false
|
||||
|
||||
strongSelf.sourceNode.restoreToNaturalSize()
|
||||
strongSelf.sourceNode.addSubnode(strongSelf.sourceNode.contentNode)
|
||||
|
||||
strongSelf.sourceNode.animatedOut?()
|
||||
|
||||
completion()
|
||||
}
|
||||
|
||||
let convertedFrame = convertFrame(self.sourceNode.bounds, from: self.sourceNode.view, to: self.view)
|
||||
self.sourceNode.contentNode.frame = convertedFrame
|
||||
self.initialSourceFrame = convertedFrame
|
||||
|
||||
if let (scale, pinchLocation, offset) = self.sourceNode.gesture.currentTransform, let initialSourceFrame = self.initialSourceFrame {
|
||||
let duration = 0.3
|
||||
let transitionCurve: ContainedViewLayoutTransitionCurve = .easeInOut
|
||||
|
||||
var updatedContentAreaInScreenSpace = self.getContentAreaInScreenSpace()
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
|
||||
self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: updatedContentAreaInScreenSpace, duration: duration * 1.0, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: updatedContentAreaInScreenSpace.minY, duration: duration * 1.0, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: .spring)
|
||||
if self.hapticFeedback == nil {
|
||||
self.hapticFeedback = HapticFeedback()
|
||||
}
|
||||
self.hapticFeedback?.prepareImpact(.light)
|
||||
self.hapticFeedback?.impact(.light)
|
||||
|
||||
self.sourceNode.scaleUpdated?(1.0, transition)
|
||||
|
||||
let pinchOffset = CGPoint(
|
||||
x: pinchLocation.x - initialSourceFrame.width / 2.0,
|
||||
y: pinchLocation.y - initialSourceFrame.height / 2.0
|
||||
)
|
||||
|
||||
var transform = CATransform3DIdentity
|
||||
transform = CATransform3DScale(transform, scale, scale, 0.0)
|
||||
|
||||
self.sourceNode.contentNode.transform = CATransform3DIdentity
|
||||
self.sourceNode.contentNode.position = CGPoint(x: initialSourceFrame.midX, y: initialSourceFrame.midY)
|
||||
self.sourceNode.contentNode.layer.animateSpring(from: scale as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration * 1.2, damping: 110.0)
|
||||
self.sourceNode.contentNode.layer.animatePosition(from: CGPoint(x: offset.x - pinchOffset.x * (scale - 1.0), y: offset.y - pinchOffset.y * (scale - 1.0)), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true, force: true, completion: { _ in
|
||||
performCompletion()
|
||||
})
|
||||
|
||||
let dimNodeTransition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: transitionCurve)
|
||||
dimNodeTransition.updateAlpha(node: self.dimNode, alpha: 0.0)
|
||||
} else {
|
||||
performCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition) {
|
||||
if self.isAnimatingOut {
|
||||
self.scrollingContainer.bounds = self.scrollingContainer.bounds.offsetBy(dx: 0.0, dy: offset.y)
|
||||
transition.animateOffsetAdditive(node: self.scrollingContainer, offset: -offset.y)
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public final class PinchControllerImpl: ViewController, PinchController, StandalonePresentableController {
|
||||
private let _ready = Promise<Bool>()
|
||||
override public var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
private let sourceNode: PinchSourceContainerNode
|
||||
private let disableScreenshots: Bool
|
||||
private let getContentAreaInScreenSpace: () -> CGRect
|
||||
|
||||
private var wasDismissed = false
|
||||
|
||||
private var controllerNode: PinchControllerNode {
|
||||
return self.displayNode as! PinchControllerNode
|
||||
}
|
||||
|
||||
public init(sourceNode: PinchSourceContainerNode, disableScreenshots: Bool = false, getContentAreaInScreenSpace: @escaping () -> CGRect) {
|
||||
self.sourceNode = sourceNode
|
||||
self.disableScreenshots = disableScreenshots
|
||||
self.getContentAreaInScreenSpace = getContentAreaInScreenSpace
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
self.lockOrientation = true
|
||||
self.blocksBackgroundWhenInOverlay = true
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = PinchControllerNode(controller: self, sourceNode: self.sourceNode, disableScreenshots: self.disableScreenshots, getContentAreaInScreenSpace: self.getContentAreaInScreenSpace)
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self._ready.set(.single(true))
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.controllerNode.updateLayout(layout: layout, transition: transition, previousActionsContainerNode: nil)
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
if self.ignoreAppearanceMethodInvocations() {
|
||||
return
|
||||
}
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
self.controllerNode.animateIn()
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
if !self.wasDismissed {
|
||||
self.wasDismissed = true
|
||||
self.controllerNode.animateOut(completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition) {
|
||||
self.controllerNode.addRelativeContentOffset(offset, transition: transition)
|
||||
}
|
||||
}
|
||||
@@ -598,6 +598,84 @@ public final class VariableBlurView: UIView {
|
||||
variableBlur.setValue("mask_source", forKey: "inputSourceSublayerName")
|
||||
variableBlur.setValue(true, forKey: "inputNormalizeEdges")
|
||||
mainEffectLayer.filters = [variableBlur]
|
||||
|
||||
/*#if DEBUG
|
||||
if let classValue = NSClassFromString("CAFilter") as AnyObject as? NSObjectProtocol {
|
||||
let makeSelector = NSSelectorFromString("filterWithName:")
|
||||
let filter = classValue.perform(makeSelector, with: "colorMatrix").takeUnretainedValue() as? NSObject
|
||||
|
||||
if let filter {
|
||||
/// Builds a 4x5 (RGBA) color matrix (20 floats, row-major).
|
||||
/// Applies: Desaturate(s) then TintToward(t, k) in linear RGB.
|
||||
/// - s: saturation in [0,1] (1 = unchanged, 0 = grayscale)
|
||||
/// - k: tint strength in [0,1] (0 = none, 1 = fully "brightness->tint" mapping)
|
||||
/// - t: tint color (linear RGB). Doesn't have to be normalized, but should be non-negative.
|
||||
/// - bias: optional constant "cast" added in the bias column, scaled by tint color.
|
||||
let makeDesatTintMatrix: (Float32, Float32, (Float32, Float32, Float32), Float32) -> [Float32] = { s, k, t, bias in
|
||||
// Rec.709 luma weights (good default for linear RGB)
|
||||
let wr: Float32 = 0.2126
|
||||
let wg: Float32 = 0.7152
|
||||
let wb: Float32 = 0.0722
|
||||
|
||||
let (tr, tg, tb) = t
|
||||
|
||||
// --- D = desaturation matrix ---
|
||||
let a = 1 - s
|
||||
|
||||
let D00 = wr * a + s, D01 = wg * a, D02 = wb * a
|
||||
let D10 = wr * a, D11 = wg * a + s, D12 = wb * a
|
||||
let D20 = wr * a, D21 = wg * a, D22 = wb * a + s
|
||||
|
||||
// --- T = outer(t, w): maps luma to tinted RGB (preserves luma per weights) ---
|
||||
let T00 = tr * wr, T01 = tr * wg, T02 = tr * wb
|
||||
let T10 = tg * wr, T11 = tg * wg, T12 = tg * wb
|
||||
let T20 = tb * wr, T21 = tb * wg, T22 = tb * wb
|
||||
|
||||
// --- A = (1-k)I + kT ---
|
||||
let ik = 1 - k
|
||||
|
||||
let A00 = ik + k * T00, A01 = k * T01, A02 = k * T02
|
||||
let A10 = k * T10, A11 = ik + k * T11, A12 = k * T12
|
||||
let A20 = k * T20, A21 = k * T21, A22 = ik + k * T22
|
||||
|
||||
// --- M = A * D (3x3 multiply) ---
|
||||
let M00 = A00 * D00 + A01 * D10 + A02 * D20
|
||||
let M01 = A00 * D01 + A01 * D11 + A02 * D21
|
||||
let M02 = A00 * D02 + A01 * D12 + A02 * D22
|
||||
|
||||
let M10 = A10 * D00 + A11 * D10 + A12 * D20
|
||||
let M11 = A10 * D01 + A11 * D11 + A12 * D21
|
||||
let M12 = A10 * D02 + A11 * D12 + A12 * D22
|
||||
|
||||
let M20 = A20 * D00 + A21 * D10 + A22 * D20
|
||||
let M21 = A20 * D01 + A21 * D11 + A22 * D21
|
||||
let M22 = A20 * D02 + A21 * D12 + A22 * D22
|
||||
|
||||
// Optional constant cast in bias column
|
||||
let bR = bias * tr
|
||||
let bG = bias * tg
|
||||
let bB = bias * tb
|
||||
|
||||
// 4x5 row-major, alpha passthrough:
|
||||
return [
|
||||
M00, M01, M02, 0, bR,
|
||||
M10, M11, M12, 0, bG,
|
||||
M20, M21, M22, 0, bB,
|
||||
0, 0, 0, 1, 0
|
||||
]
|
||||
}
|
||||
|
||||
var matrix: [Float32] = makeDesatTintMatrix(
|
||||
0.1, // more desaturated
|
||||
0.1, // moderate tinting
|
||||
(0.0, 1.0, 0.0), // warm tint target
|
||||
0.0 // set e.g. 0.03 for a gentle warm cast
|
||||
)
|
||||
filter.setValue(NSValue(bytes: &matrix, objCType: "{CAColorMatrix=ffffffffffffffffffff}"), forKey: "inputColorMatrix")
|
||||
mainEffectLayer.filters = [variableBlur, filter]
|
||||
}
|
||||
}
|
||||
#endif*/
|
||||
}
|
||||
} else {
|
||||
self.additionalEffectLayer = createBackdropLayer()
|
||||
|
||||
@@ -48,6 +48,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/ContextControllerImpl",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ import TelegramCore
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import ContextUI
|
||||
import ContextControllerImpl
|
||||
|
||||
final class GiftAttributeListContextItem: ContextMenuCustomItem {
|
||||
let context: AccountContext
|
||||
|
||||
+4
-4
@@ -511,7 +511,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
self.scrollToTop()
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(GiftStoreReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(GiftStoreReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
contextController.dismissed = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
@@ -613,7 +613,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
), false))
|
||||
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
context: component.context,
|
||||
presentationData: presentationData,
|
||||
source: .reference(GiftStoreReferenceContentSource(controller: controller, sourceView: sourceView)),
|
||||
@@ -721,7 +721,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
), false))
|
||||
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
context: component.context,
|
||||
presentationData: presentationData,
|
||||
source: .reference(GiftStoreReferenceContentSource(controller: controller, sourceView: sourceView)),
|
||||
@@ -829,7 +829,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
), false))
|
||||
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
context: component.context,
|
||||
presentationData: presentationData,
|
||||
source: .reference(GiftStoreReferenceContentSource(controller: controller, sourceView: sourceView)),
|
||||
|
||||
+1
-1
@@ -1831,7 +1831,7 @@ private final class GiftAuctionBidScreenComponent: Component {
|
||||
self?.share()
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(GiftViewContextReferenceContentSource(controller: controller, sourceView: view)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(GiftViewContextReferenceContentSource(controller: controller, sourceView: view)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -459,7 +459,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
||||
self?.share()
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(GiftViewContextReferenceContentSource(controller: controller, sourceView: view)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(GiftViewContextReferenceContentSource(controller: controller, sourceView: view)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
|
||||
@@ -1530,7 +1530,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(GiftViewContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(GiftViewContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
})
|
||||
}
|
||||
|
||||
+141
-4
@@ -424,7 +424,9 @@ public class GlassBackgroundView: UIView {
|
||||
nativeView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
} else {
|
||||
let nativeFrame = CGRect(origin: CGPoint(), size: size)
|
||||
transition.setFrame(view: nativeView, frame: nativeFrame)
|
||||
transition.animateView {
|
||||
nativeView.frame = nativeFrame
|
||||
}
|
||||
}
|
||||
nativeView.overrideUserInterfaceStyle = isDark ? .dark : .light
|
||||
}
|
||||
@@ -544,7 +546,7 @@ public class GlassBackgroundView: UIView {
|
||||
if transition.animation.isImmediate {
|
||||
nativeView.effect = nil
|
||||
} else {
|
||||
UIView.animate {
|
||||
transition.animateView {
|
||||
nativeView.effect = nil
|
||||
}
|
||||
}
|
||||
@@ -555,7 +557,7 @@ public class GlassBackgroundView: UIView {
|
||||
} else {
|
||||
if let glassEffect, let currentEffect = nativeView.effect as? UIGlassEffect, currentEffect.tintColor == glassEffect.tintColor, currentEffect.isInteractive == glassEffect.isInteractive {
|
||||
} else {
|
||||
UIView.animate {
|
||||
transition.animateView {
|
||||
nativeView.effect = glassEffect
|
||||
}
|
||||
}
|
||||
@@ -680,7 +682,9 @@ public final class GlassBackgroundContainerView: UIView {
|
||||
nativeParamsView.lumaMax = 0.801
|
||||
}
|
||||
|
||||
transition.setFrame(view: nativeView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.animateView {
|
||||
nativeView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
}
|
||||
} else if let legacyView = self.legacyView {
|
||||
transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
@@ -1082,3 +1086,136 @@ public final class GlassBackgroundComponent: Component {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public final class GlassContextExtractableContainer: UIView, ContextExtractableContainer {
|
||||
private struct NormalParams {
|
||||
let size: CGSize
|
||||
let cornerRadius: CGFloat
|
||||
let isDark: Bool
|
||||
let tintColor: GlassBackgroundView.TintColor
|
||||
let isInteractive: Bool
|
||||
let isVisible: Bool
|
||||
|
||||
init(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: GlassBackgroundView.TintColor, isInteractive: Bool, isVisible: Bool) {
|
||||
self.size = size
|
||||
self.cornerRadius = cornerRadius
|
||||
self.isDark = isDark
|
||||
self.tintColor = tintColor
|
||||
self.isInteractive = isInteractive
|
||||
self.isVisible = isVisible
|
||||
}
|
||||
}
|
||||
|
||||
public let extractableContentView: UIView
|
||||
public let normalContentView: UIView
|
||||
|
||||
public var contentView: UIView {
|
||||
return self.normalContentView
|
||||
}
|
||||
|
||||
private let glassView: GlassBackgroundView
|
||||
|
||||
private var state: State = .normal
|
||||
private var normalParams: NormalParams?
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
self.extractableContentView = UIView()
|
||||
self.glassView = GlassBackgroundView()
|
||||
self.normalContentView = SparseContainerView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.glassView.contentView.addSubview(self.normalContentView)
|
||||
self.extractableContentView.addSubview(self.glassView)
|
||||
self.addSubview(self.extractableContentView)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.isUserInteractionEnabled {
|
||||
return nil
|
||||
}
|
||||
if self.isHidden {
|
||||
return nil
|
||||
}
|
||||
if self.alpha == 0.0 {
|
||||
return nil
|
||||
}
|
||||
switch self.state {
|
||||
case .normal:
|
||||
if let result = self.normalContentView.hitTest(self.convert(point, to: self.normalContentView), with: event) {
|
||||
return result
|
||||
}
|
||||
case .extracted:
|
||||
break
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: GlassBackgroundView.TintColor, isInteractive: Bool = false, isVisible: Bool = true, transition: ComponentTransition) {
|
||||
let normalParams = NormalParams(size: size, cornerRadius: cornerRadius, isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, isVisible: isVisible)
|
||||
self.normalParams = normalParams
|
||||
|
||||
if case .normal = self.state {
|
||||
self.applyState(transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateState(state: State, transition: ContainedViewLayoutTransition) {
|
||||
self.state = state
|
||||
self.applyState(transition: ComponentTransition(transition))
|
||||
}
|
||||
|
||||
private func applyState(transition: ComponentTransition) {
|
||||
guard let normalParams = self.normalParams else {
|
||||
return
|
||||
}
|
||||
switch self.state {
|
||||
case .normal:
|
||||
transition.setAlpha(view: self.normalContentView, alpha: 1.0)
|
||||
transition.setFrame(view: self.extractableContentView, frame: CGRect(origin: CGPoint(), size: normalParams.size))
|
||||
transition.setFrame(view: self.normalContentView, frame: CGRect(origin: CGPoint(), size: normalParams.size))
|
||||
|
||||
self.glassView.update(
|
||||
size: normalParams.size,
|
||||
cornerRadius: normalParams.cornerRadius,
|
||||
isDark: normalParams.isDark,
|
||||
tintColor: normalParams.tintColor,
|
||||
isInteractive: normalParams.isInteractive,
|
||||
isVisible: normalParams.isVisible,
|
||||
transition: transition
|
||||
)
|
||||
case let .extracted(size, cornerRadius, extractionState):
|
||||
switch extractionState {
|
||||
case .animatedOut:
|
||||
transition.setAlpha(view: self.normalContentView, alpha: 1.0)
|
||||
|
||||
self.glassView.update(
|
||||
size: normalParams.size,
|
||||
cornerRadius: normalParams.cornerRadius,
|
||||
isDark: normalParams.isDark,
|
||||
tintColor: normalParams.tintColor,
|
||||
isInteractive: normalParams.isInteractive,
|
||||
isVisible: normalParams.isVisible,
|
||||
transition: transition
|
||||
)
|
||||
case .animatedIn:
|
||||
transition.setAlpha(view: self.normalContentView, alpha: 0.0)
|
||||
|
||||
self.glassView.update(
|
||||
size: size,
|
||||
cornerRadius: cornerRadius,
|
||||
isDark: normalParams.isDark,
|
||||
tintColor: normalParams.tintColor,
|
||||
isInteractive: normalParams.isInteractive,
|
||||
isVisible: normalParams.isVisible,
|
||||
transition: transition
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -435,7 +435,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
|
||||
updateTimeout(nil)
|
||||
})))
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(sourceView: sourceView, position: self.currentIsCaptionAbove ? .bottom : .top)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(sourceView: sourceView, position: self.currentIsCaptionAbove ? .bottom : .top)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
self.present(contextController)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ swift_library(
|
||||
"//submodules/TelegramNotices",
|
||||
"//submodules/TelegramUI/Components/AttachmentFileController",
|
||||
"//submodules/SaveToCameraRoll",
|
||||
"//submodules/TelegramUI/Components/ContextControllerImpl",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -25,7 +25,7 @@ func presentLinkOptionsController(context: AccountContext, selfController: Creat
|
||||
return
|
||||
}
|
||||
|
||||
let contextController = ContextController(
|
||||
let contextController = makeContextController(
|
||||
presentationData: context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme),
|
||||
configuration: ContextController.Configuration(
|
||||
sources: sources,
|
||||
|
||||
@@ -5406,7 +5406,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
}
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme)
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: UIScreen.main.bounds, customPosition: CGPoint(x: 0.0, y: -3.0))), items: .single(ContextController.Items(content: .list(items))))
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: UIScreen.main.bounds, customPosition: CGPoint(x: 0.0, y: -3.0))), items: .single(ContextController.Items(content: .list(items))))
|
||||
self.controller?.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
@@ -7224,7 +7224,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
self?.node.presentAudioPicker()
|
||||
})))
|
||||
|
||||
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.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
@@ -7300,7 +7300,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
)))
|
||||
}
|
||||
|
||||
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.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
@@ -7916,7 +7916,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
portalView.view.layer.rasterizationScale = UIScreenScale
|
||||
self.node.previewContentContainerView.addPortal(view: portalView)
|
||||
|
||||
let stickerResultController = PeekController(
|
||||
let stickerResultController = makePeekController(
|
||||
presentationData: presentationData,
|
||||
content: StickerPreviewPeekContent(
|
||||
context: self.context,
|
||||
|
||||
+1
@@ -9,6 +9,7 @@ import AccountContext
|
||||
import TelegramPresentationData
|
||||
import StickerResources
|
||||
import ContextUI
|
||||
import ContextControllerImpl
|
||||
|
||||
final class StickerPackListContextItem: ContextMenuCustomItem {
|
||||
let context: AccountContext
|
||||
|
||||
+1
-1
@@ -586,7 +586,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi
|
||||
let items = self.contextMenuSpeedItems(scheduleTooltip: { change in
|
||||
scheduledTooltip = change
|
||||
})
|
||||
let contextController = ContextController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items, gesture: gesture)
|
||||
let contextController = makeContextController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items, gesture: gesture)
|
||||
contextController.dismissed = { [weak self] in
|
||||
if let scheduledTooltip, let self, let rate = self.playbackBaseRate {
|
||||
self.setRate?(rate, scheduledTooltip)
|
||||
|
||||
+1
-1
@@ -290,7 +290,7 @@ final class StickersResultPanelComponent: Component {
|
||||
}, present: { [weak self] content, sourceView, sourceRect in
|
||||
if let self, let component = self.component {
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: component.theme)
|
||||
let controller = PeekController(presentationData: presentationData, content: content, sourceView: {
|
||||
let controller = makePeekController(presentationData: presentationData, content: content, sourceView: {
|
||||
return (sourceView, sourceRect)
|
||||
})
|
||||
component.presentInGlobalOverlay(controller)
|
||||
|
||||
@@ -550,8 +550,8 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
|
||||
|
||||
public let backgroundNode: NavigationBackgroundNode
|
||||
|
||||
private var leftButtonsBackgroundView: (background: GlassBackgroundView, container: UIView)?
|
||||
private var rightButtonsBackgroundView: (background: GlassBackgroundView, container: UIView)?
|
||||
private var leftButtonsBackgroundView: (background: GlassContextExtractableContainer, container: UIView)?
|
||||
private var rightButtonsBackgroundView: (background: GlassContextExtractableContainer, container: UIView)?
|
||||
|
||||
private let backButtonNodeImpl: NavigationButtonNodeImpl
|
||||
public var backButtonNode: NavigationButtonNode {
|
||||
@@ -662,12 +662,12 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
|
||||
|
||||
backgroundContainer.contentView.addSubview(self.customOverBackgroundContentView)
|
||||
|
||||
let leftButtonsBackgroundView: (background: GlassBackgroundView, container: UIView) = (GlassBackgroundView(), UIView())
|
||||
let leftButtonsBackgroundView: (background: GlassContextExtractableContainer, container: UIView) = (GlassContextExtractableContainer(), UIView())
|
||||
leftButtonsBackgroundView.background.contentView.addSubview(leftButtonsBackgroundView.container)
|
||||
self.leftButtonsBackgroundView = leftButtonsBackgroundView
|
||||
backgroundContainer.contentView.addSubview(leftButtonsBackgroundView.background)
|
||||
|
||||
let rightButtonsBackgroundView: (background: GlassBackgroundView, container: UIView) = (GlassBackgroundView(), UIView())
|
||||
let rightButtonsBackgroundView: (background: GlassContextExtractableContainer, container: UIView) = (GlassContextExtractableContainer(), UIView())
|
||||
rightButtonsBackgroundView.background.contentView.addSubview(rightButtonsBackgroundView.container)
|
||||
self.rightButtonsBackgroundView = rightButtonsBackgroundView
|
||||
backgroundContainer.contentView.addSubview(rightButtonsBackgroundView.background)
|
||||
@@ -1062,6 +1062,16 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
|
||||
}
|
||||
}
|
||||
|
||||
public func navigationButtonContextContainer(sourceView: UIView) -> ContextExtractableContainer? {
|
||||
if let leftButtonsBackgroundView = self.leftButtonsBackgroundView, sourceView.isDescendant(of: leftButtonsBackgroundView.background) {
|
||||
return leftButtonsBackgroundView.background
|
||||
}
|
||||
if let rightButtonsBackgroundView = self.rightButtonsBackgroundView, sourceView.isDescendant(of: rightButtonsBackgroundView.background) {
|
||||
return rightButtonsBackgroundView.background
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public var intrinsicCanTransitionInline: Bool = true
|
||||
|
||||
public var passthroughTouches = true
|
||||
|
||||
+2
-2
@@ -505,7 +505,7 @@ final class AffiliateProgramSetupScreenComponent: Component {
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, actionsOnTop: false)), items: .single(ContextController.Items(id: AnyHashable(0), content: .list(items))), gesture: nil)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, actionsOnTop: false)), items: .single(ContextController.Items(id: AnyHashable(0), content: .list(items))), gesture: nil)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
@@ -1343,7 +1343,7 @@ final class AffiliateProgramSetupScreenComponent: Component {
|
||||
|
||||
let items = ContextController.Items(content: .list(itemList))
|
||||
|
||||
let controller = ContextController(
|
||||
let controller = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(ListContextExtractedContentSource(contentView: sourceView)),
|
||||
items: .single(items),
|
||||
|
||||
+1
-1
@@ -450,7 +450,7 @@ private final class JoinAffiliateProgramScreenComponent: Component {
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, actionsOnTop: true)), items: .single(ContextController.Items(id: AnyHashable(0), content: .list(items))), gesture: nil)
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, actionsOnTop: true)), items: .single(ContextController.Items(id: AnyHashable(0), content: .list(items))), gesture: nil)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user