Attach menu improvements

This commit is contained in:
Ilya Laktyushin 2022-02-22 18:02:14 +03:00
parent 59595cb027
commit 3ddf3518c5
18 changed files with 254 additions and 45 deletions

View File

@ -3,7 +3,7 @@ import Display
import SwiftSignalKit
public protocol ContactSelectionController: ViewController {
var result: Signal<([ContactListPeer], ContactListAction)?, NoError> { get }
var result: Signal<([ContactListPeer], ContactListAction, Bool, Int32?)?, NoError> { get }
var displayProgress: Bool { get set }
var dismissed: (() -> Void)? { get set }

View File

@ -273,7 +273,7 @@ public class AttachmentController: ViewController {
self.container.position = startPosition
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
transition.animateView({
transition.animateView(allowUserInteraction: true, {
self.container.position = targetPosition
})
}

View File

@ -584,7 +584,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
if textInputPanelNode.frame.width.isZero {
panelTransition = .immediate
}
let panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: insets.left, rightInset: insets.right, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics)
let panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: insets.left + layout.safeInsets.left, rightInset: insets.right + layout.safeInsets.right, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics)
let panelFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: panelHeight)
if textInputPanelNode.frame.width.isZero {
textInputPanelNode.frame = panelFrame

View File

@ -385,7 +385,7 @@ public final class CallListController: TelegramBaseController {
|> take(1)
|> deliverOnMainQueue).start(next: { [weak controller, weak self] result in
controller?.dismissSearch()
if let strongSelf = self, let (contactPeers, action) = result, let contactPeer = contactPeers.first, case let .peer(peer, _, _) = contactPeer {
if let strongSelf = self, let (contactPeers, action, _, _) = result, let contactPeer = contactPeers.first, case let .peer(peer, _, _) = contactPeer {
strongSelf.call(peer.id, isVideo: action == .videoCall, began: {
if let strongSelf = self {
let _ = (strongSelf.context.sharedContext.hasOngoingCall.get()

View File

@ -801,6 +801,9 @@ public final class ContactListNode: ASDisplayNode {
public var selectionState: ContactListNodeGroupSelectionState? {
return self.selectionStateValue
}
public var selectionStateSignal: Signal<ContactListNodeGroupSelectionState?, NoError> {
return self.selectionStatePromise.get()
}
public var selectionStateUpdated: ((ContactListNodeGroupSelectionState?) -> Void)?
public var selectedPeers: [ContactListPeer] {

View File

@ -1440,13 +1440,17 @@ public struct CombinedTransition {
}
public extension ContainedViewLayoutTransition {
func animateView(_ f: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
func animateView(allowUserInteraction: Bool = false, _ f: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
switch self {
case .immediate:
f()
completion?(true)
case let .animated(duration, curve):
UIView.animate(withDuration: duration, delay: 0.0, options: curve.viewAnimationOptions, animations: {
var options = curve.viewAnimationOptions
if allowUserInteraction {
options.insert(.allowUserInteraction)
}
UIView.animate(withDuration: duration, delay: 0.0, options: options, animations: {
f()
}, completion: completion)
}

View File

@ -23,16 +23,18 @@ final class MediaPickerInteraction {
let toggleSelection: (TGMediaSelectableItem, Bool) -> Void
let sendSelected: (TGMediaSelectableItem?, Bool, Int32?, Bool) -> Void
let schedule: () -> Void
let dismissInput: () -> Void
let selectionState: TGMediaSelectionContext?
let editingState: TGMediaEditingContext
var hiddenMediaId: String?
init(openMedia: @escaping (PHFetchResult<PHAsset>, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool) -> Void, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool) -> Void, schedule: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) {
init(openMedia: @escaping (PHFetchResult<PHAsset>, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool) -> Void, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool) -> Void, schedule: @escaping () -> Void, dismissInput: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) {
self.openMedia = openMedia
self.openSelectedMedia = openSelectedMedia
self.toggleSelection = toggleSelection
self.sendSelected = sendSelected
self.schedule = schedule
self.dismissInput = dismissInput
self.selectionState = selectionState
self.editingState = editingState
}
@ -108,6 +110,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private var presentationData: PresentationData
private let mediaAssetsContext: MediaAssetsContext
private let backgroundNode: NavigationBackgroundNode
private let gridNode: GridNode
private var cameraView: TGAttachmentCameraView?
private var placeholderNode: MediaPickerPlaceholderNode?
@ -142,12 +145,14 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
let mediaAssetsContext = MediaAssetsContext()
self.mediaAssetsContext = mediaAssetsContext
self.backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.tabBar.backgroundColor)
self.gridNode = GridNode()
super.init()
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.addSubnode(self.backgroundNode)
self.addSubnode(self.gridNode)
self.interaction = MediaPickerInteraction(openMedia: { [weak self] fetchResult, index, immediateThumbnail in
@ -171,6 +176,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self?.interaction?.sendSelected(nil, false, time, true)
})
}
}, dismissInput: { [weak self] in
if let strongSelf = self {
strongSelf.dismissInput()
}
}, selectionState: TGMediaSelectionContext(), editingState: TGMediaEditingContext())
self.interaction?.selectionState?.grouping = true
@ -281,6 +290,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
cameraView.startPreview()
self.gridNode.scrollView.addSubview(cameraView)
// self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate)
}
private func dismissInput() {
@ -590,6 +601,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
let gridInsets = UIEdgeInsets(top: insets.top + manageHeight, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right)
transition.updateFrame(node: self.gridNode, frame: bounds)
transition.updateFrame(node: self.backgroundNode, frame: bounds)
self.backgroundNode.update(size: bounds.size, transition: transition)
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: bounds.size, insets: gridInsets, scrollIndicatorInsets: nil, preloadSize: 200.0, type: .fixed(itemSize: CGSize(width: itemWidth, height: itemWidth), fillWidth: true, lineSpacing: itemSpacing, itemSpacing: itemSpacing), cutout: cameraRect), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { [weak self] _ in
guard let strongSelf = self else {
return
@ -696,7 +710,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.moreButtonNode = MediaPickerMoreButtonNode(theme: self.presentationData.theme)
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData))
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData, hideBackground: false, hideBadge: false, hideSeparator: true))
self.statusBar.statusBarStyle = .Ignore

View File

@ -189,7 +189,7 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
}
}
final class MediaPickerSelectedListNode: ASDisplayNode {
final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate {
private let context: AccountContext
fileprivate let wallpaperBackgroundNode: WallpaperBackgroundNode
@ -225,6 +225,8 @@ final class MediaPickerSelectedListNode: ASDisplayNode {
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
}
self.scrollNode.view.delegate = self
self.view.addGestureRecognizer(ReorderingGestureRecognizer(shouldBegin: { [weak self] point in
if let strongSelf = self, !strongSelf.scrollNode.view.isTracking {
for (_, itemNode) in strongSelf.itemNodes {
@ -246,6 +248,10 @@ final class MediaPickerSelectedListNode: ASDisplayNode {
}))
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.interaction?.dismissInput()
}
func scrollToTop(animated: Bool) {
self.scrollNode.view.setContentOffset(CGPoint(), animated: animated)
}
@ -399,9 +405,21 @@ final class MediaPickerSelectedListNode: ASDisplayNode {
for i in stride(from: 0, to: itemSizes.count, by: groupSize) {
let sizes = itemSizes[i ..< min(i + groupSize, itemSizes.count)]
let items = items[i ..< min(i + groupSize, items.count)]
if items.count > 1 {
let (mosaicLayout, size) = chatMessageBubbleMosaicLayout(maxSize: boundingSize, itemSizes: Array(sizes), spacing: 1.0, fillWidth: true)
let layout = zip(items, mosaicLayout).map { ($0, $1.0, $1.1) }
groupLayouts.append((layout, size))
} else if let item = items.first, var itemSize = sizes.first {
if itemSize.width > itemSize.height {
itemSize = itemSize.aspectFitted(boundingSize)
} else {
itemSize = boundingSize
}
let itemRect = CGRect(origin: CGPoint(), size: itemSize)
let position: MosaicItemPosition = [.top, .bottom, .left, .right]
groupLayouts.append(([(item, itemRect, position)], itemRect.size))
}
}
} else {
for i in 0 ..< itemSizes.count {

View File

@ -1322,7 +1322,7 @@ private func addContactToExisting(context: AccountContext, parentController: Vie
(parentController.navigationController as? NavigationController)?.pushViewController(contactsController)
let _ = (contactsController.result
|> deliverOnMainQueue).start(next: { result in
if let (peers, _) = result, let peer = peers.first {
if let (peers, _, _, _) = result, let peer = peers.first {
let dataSignal: Signal<(Peer?, DeviceContactStableId?), NoError>
switch peer {
case let .peer(contact, _, _):

View File

@ -63,10 +63,10 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
icon = UIImage(bundleImageName: "Chat/Context Menu/ReportCopyright")
case .illegalDrugs:
title = presentationData.strings.ReportPeer_ReasonIllegalDrugs
icon = UIImage(bundleImageName: "Chat/Context Menu/ReportCopyright")
icon = UIImage(bundleImageName: "Chat/Context Menu/ReportDrugs")
case .personalDetails:
title = presentationData.strings.ReportPeer_ReasonPersonalDetails
icon = UIImage(bundleImageName: "Chat/Context Menu/ReportCopyright")
icon = UIImage(bundleImageName: "Chat/Context Menu/User")
case .other:
title = presentationData.strings.ReportPeer_ReasonOther
icon = UIImage(bundleImageName: "Chat/Context Menu/Report")

View File

@ -46,9 +46,9 @@ public extension ToolbarTheme {
}
public extension NavigationBarTheme {
convenience init(rootControllerTheme: PresentationTheme, enableBackgroundBlur: Bool = true, hideBackground: Bool = false, hideBadge: Bool = false) {
convenience init(rootControllerTheme: PresentationTheme, enableBackgroundBlur: Bool = true, hideBackground: Bool = false, hideBadge: Bool = false, hideSeparator: Bool = false) {
let theme = rootControllerTheme.rootController.navigationBar
self.init(buttonColor: theme.buttonColor, disabledButtonColor: theme.disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : theme.badgeBackgroundColor, badgeStrokeColor: hideBadge ? .clear : theme.badgeStrokeColor, badgeTextColor: hideBadge ? .clear : theme.badgeTextColor)
self.init(buttonColor: theme.buttonColor, disabledButtonColor: theme.disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground || hideSeparator ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : theme.badgeBackgroundColor, badgeStrokeColor: hideBadge ? .clear : theme.badgeStrokeColor, badgeTextColor: hideBadge ? .clear : theme.badgeTextColor)
}
}
@ -63,8 +63,8 @@ public extension NavigationBarPresentationData {
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
}
convenience init(presentationData: PresentationData, hideBackground: Bool, hideBadge: Bool) {
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground, hideBadge: hideBadge), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
convenience init(presentationData: PresentationData, hideBackground: Bool, hideBadge: Bool, hideSeparator: Bool = false) {
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground, hideBadge: hideBadge, hideSeparator: hideSeparator), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
}
convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings) {

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "drugs_24.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,97 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
0.707107 -0.707107 0.707107 0.707107 -0.794397 7.217199 cm
0.000000 0.000000 0.000000 scn
5.665000 23.093994 m
2.536307 23.093994 0.000000 20.557688 0.000000 17.428993 c
0.000000 11.626219 l
0.000000 10.428994 l
0.000000 7.428994 l
0.000000 4.300301 2.536307 1.763992 5.665000 1.763992 c
8.793693 1.763992 11.330000 4.300301 11.330000 7.428994 c
11.330000 10.428994 l
11.330000 11.626219 l
11.330000 17.428993 l
11.330000 20.557688 8.793693 23.093994 5.665000 23.093994 c
h
10.000000 13.456901 m
10.000000 17.428993 l
10.000000 19.823149 8.059155 21.763994 5.665000 21.763994 c
3.270846 21.763994 1.330000 19.823149 1.330000 17.428993 c
1.330000 13.456901 l
2.192297 13.760678 3.608150 14.093994 5.665000 14.093994 c
7.721851 14.093994 9.137704 13.760677 10.000000 13.456901 c
h
1.330000 11.626219 m
1.330000 10.428994 l
1.330000 7.428994 l
1.330000 5.034840 3.270846 3.093994 5.665000 3.093994 c
8.059155 3.093994 10.000000 5.034840 10.000000 7.428994 c
10.000000 10.428994 l
10.000000 11.626219 l
10.000000 11.915585 9.860453 12.089630 9.704794 12.148787 c
9.019821 12.409109 7.712020 12.763994 5.665000 12.763994 c
3.617980 12.763994 2.310179 12.409109 1.625207 12.148788 c
1.469548 12.089630 1.330000 11.915585 1.330000 11.626219 c
h
f*
n
Q
endstream
endobj
3 0 obj
1275
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001365 00000 n
0000001388 00000 n
0000001561 00000 n
0000001635 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1694
%%EOF

View File

@ -10325,7 +10325,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let currentLocationController = Atomic<AttachmentContainable?>(value: nil)
var canSendPolls = true
if let _ = peer as? TelegramUser {
if peer is TelegramUser || peer is TelegramSecretChat {
canSendPolls = false
} else if let channel = peer as? TelegramChannel {
if channel.hasBannedPermission(.banSendPolls) != nil {
@ -10434,11 +10434,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
case .contact:
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true))
contactsController.presentScheduleTimePicker = { [weak self] completion in
if let strongSelf = self {
strongSelf.presentScheduleTimePicker(completion: completion)
}
}
contactsController.navigationPresentation = .modal
completion(contactsController, nil)
completion(contactsController, contactsController.mediaPickerContext)
strongSelf.controllerNavigationDisposable.set((contactsController.result
|> deliverOnMainQueue).start(next: { [weak self] peers in
if let strongSelf = self, let (peers, _) = peers {
if let strongSelf = self, let (peers, _, silent, scheduleTime) = peers {
if peers.count > 1 {
var enqueueMessages: [EnqueueMessage] = []
for peer in peers {
@ -10475,7 +10480,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
enqueueMessages.append(message)
}
}
strongSelf.sendMessages(enqueueMessages)
strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
} else if let peer = peers.first {
let dataSignal: Signal<(Peer?, DeviceContactExtendedData?), NoError>
switch peer {
@ -10531,7 +10536,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}, nil)
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
strongSelf.sendMessages([message])
strongSelf.sendMessages(strongSelf.transformEnqueueMessages([message], silentPosting: silent, scheduleTime: scheduleTime))
} else {
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
guard let strongSelf = self, !contactData.basicData.phoneNumbers.isEmpty else {
@ -10549,7 +10554,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}, nil)
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
strongSelf.sendMessages([message])
strongSelf.sendMessages(strongSelf.transformEnqueueMessages([message], silentPosting: silent, scheduleTime: scheduleTime))
}
}), completed: nil, cancelled: nil)
strongSelf.effectiveNavigationController?.pushViewController(contactController)
@ -11267,7 +11272,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.effectiveNavigationController?.pushViewController(contactsController)
self.controllerNavigationDisposable.set((contactsController.result
|> deliverOnMainQueue).start(next: { [weak self] peers in
if let strongSelf = self, let (peers, _) = peers {
if let strongSelf = self, let (peers, _, _, _) = peers {
if peers.count > 1 {
var enqueueMessages: [EnqueueMessage] = []
for peer in peers {

View File

@ -157,7 +157,7 @@ public class ComposeControllerImpl: ViewController, ComposeController {
strongSelf.createActionDisposable.set((controller.result
|> take(1)
|> deliverOnMainQueue).start(next: { [weak controller] result in
if let strongSelf = self, let (contactPeers, _) = result, case let .peer(peer, _, _) = contactPeers.first {
if let strongSelf = self, let (contactPeers, _, _, _) = result, case let .peer(peer, _, _) = contactPeers.first {
controller?.dismissSearch()
controller?.displayNavigationActivity = true
strongSelf.createActionDisposable.set((strongSelf.context.engine.peers.createSecretChat(peerId: peer.id) |> deliverOnMainQueue).start(next: { peerId in

View File

@ -17,7 +17,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
private let context: AccountContext
private let autoDismiss: Bool
private var contactsNode: ContactSelectionControllerNode {
fileprivate var contactsNode: ContactSelectionControllerNode {
return self.displayNode as! ContactSelectionControllerNode
}
@ -43,14 +43,16 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
return self._ready
}
private let _result = Promise<([ContactListPeer], ContactListAction)?>()
var result: Signal<([ContactListPeer], ContactListAction)?, NoError> {
private let _result = Promise<([ContactListPeer], ContactListAction, Bool, Int32?)?>()
var result: Signal<([ContactListPeer], ContactListAction, Bool, Int32?)?, NoError> {
return self._result.get()
}
private let confirmation: (ContactListPeer) -> Signal<Bool, NoError>
var dismissed: (() -> Void)?
var presentScheduleTimePicker: (@escaping (Int32) -> Void) -> Void = { _ in }
private let createActionDisposable = MetaDisposable()
private let confirmationDisposable = MetaDisposable()
@ -215,10 +217,10 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
}
}
self.contactsNode.requestMultipleAction = { [weak self] in
self.contactsNode.requestMultipleAction = { [weak self] silent, scheduleTime in
if let strongSelf = self {
let selectedPeers = strongSelf.contactsNode.contactListNode.selectedPeers
strongSelf._result.set(.single((selectedPeers, .generic)))
strongSelf._result.set(.single((selectedPeers, .generic, silent, scheduleTime)))
if strongSelf.autoDismiss {
strongSelf.dismiss()
}
@ -313,7 +315,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
self.confirmationDisposable.set((self.confirmation(peer) |> deliverOnMainQueue).start(next: { [weak self] value in
if let strongSelf = self {
if value {
strongSelf._result.set(.single(([peer], action)))
strongSelf._result.set(.single(([peer], action, false, nil)))
if strongSelf.autoDismiss {
strongSelf.dismiss()
}
@ -325,6 +327,10 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
func dismissSearch() {
self.deactivateSearch()
}
public var mediaPickerContext: AttachmentMediaPickerContext {
return ContactsPickerContext(controller: self)
}
}
private let searchBarFont = Font.regular(17.0)
@ -380,3 +386,40 @@ final class ContactsSearchNavigationContentNode: NavigationBarContentNode {
self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: false), strings: presentationData.strings)
}
}
final class ContactsPickerContext: AttachmentMediaPickerContext {
private weak var controller: ContactSelectionControllerImpl?
var selectionCount: Signal<Int, NoError> {
if let controller = self.controller {
return controller.contactsNode.contactListNode.selectionStateSignal
|> map { state in
return state?.selectedPeerIndices.count ?? 0
}
} else {
return .single(0)
}
}
var caption: Signal<NSAttributedString?, NoError> {
return .single(nil)
}
init(controller: ContactSelectionControllerImpl) {
self.controller = controller
}
func setCaption(_ caption: NSAttributedString) {
}
func send(silently: Bool, mode: AttachmentMediaPickerSendMode) {
self.controller?.contactsNode.requestMultipleAction?(silently, nil)
}
func schedule() {
self.controller?.presentScheduleTimePicker ({ time in
self.controller?.contactsNode.requestMultipleAction?(false, time)
})
}
}

View File

@ -36,7 +36,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
var requestDeactivateSearch: (() -> Void)?
var requestOpenPeerFromSearch: ((ContactListPeer) -> Void)?
var requestMultipleAction: (() -> Void)?
var requestMultipleAction: ((_ silent: Bool, _ scheduleTime: Int32?) -> Void)?
var dismiss: (() -> Void)?
var presentationData: PresentationData {
@ -58,7 +58,10 @@ final class ContactSelectionControllerNode: ASDisplayNode {
self.displayDeviceContacts = displayDeviceContacts
self.displayCallIcons = displayCallIcons
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false)), displayCallIcons: displayCallIcons, multipleSelection: multipleSelection)
var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false)), displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture in
contextActionImpl?(peer, node, gesture)
} : nil, multipleSelection: multipleSelection)
self.dimNode = ASDisplayNode()
@ -98,7 +101,17 @@ final class ContactSelectionControllerNode: ASDisplayNode {
}
shareImpl = { [weak self] in
self?.requestMultipleAction?()
self?.requestMultipleAction?(false, nil)
}
contextActionImpl = { [weak self] peer, node, gesture in
if let strongSelf = self, (strongSelf.selectionState?.selectedPeerIndices.isEmpty ?? true) {
strongSelf.contactListNode.updateSelectionState { state in
let peerId = ContactListPeerId.peer(peer.id)
let state = state ?? ContactListNodeGroupSelectionState()
return state.withToggledPeerId(peerId).withSelectedPeerMap([peerId: ContactListPeer.peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil)])
}
}
}
}
@ -134,13 +147,13 @@ final class ContactSelectionControllerNode: ASDisplayNode {
self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size)
let countPanelHeight = self.countPanelNode.updateLayout(width: layout.size.width, sideInset: layout.safeInsets.left, bottomInset: layout.intrinsicInsets.bottom, transition: transition)
if (self.selectionState?.selectedPeerIndices.isEmpty ?? true) {
transition.updateFrame(node: self.countPanelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: CGSize(width: layout.size.width, height: countPanelHeight)))
} else {
insets.bottom += countPanelHeight
transition.updateFrame(node: self.countPanelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - countPanelHeight), size: CGSize(width: layout.size.width, height: countPanelHeight)))
}
// let countPanelHeight = self.countPanelNode.updateLayout(width: layout.size.width, sideInset: layout.safeInsets.left, bottomInset: layout.intrinsicInsets.bottom, transition: transition)
// if (self.selectionState?.selectedPeerIndices.isEmpty ?? true) {
// transition.updateFrame(node: self.countPanelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: CGSize(width: layout.size.width, height: countPanelHeight)))
// } else {
// insets.bottom += countPanelHeight
// transition.updateFrame(node: self.countPanelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - countPanelHeight), size: CGSize(width: layout.size.width, height: countPanelHeight)))
// }
if let searchDisplayController = self.searchDisplayController {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)

View File

@ -8059,7 +8059,7 @@ func presentAddMembers(context: AccountContext, updatedPresentationData: (initia
if let contactsController = contactsController as? ContactSelectionController {
selectAddMemberDisposable.set((contactsController.result
|> deliverOnMainQueue).start(next: { [weak contactsController] result in
guard let (peers, _) = result, let memberPeer = peers.first else {
guard let (peers, _, _, _) = result, let memberPeer = peers.first else {
return
}