mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit 'f9f9f20141efba933f6159243df5f19f8cacb024'
This commit is contained in:
commit
0befe4d8bc
@ -7307,25 +7307,13 @@ Sorry for the inconvenience.";
|
||||
"Attachment.OpenCamera" = "Open Camera";
|
||||
|
||||
"Attachment.DeselectedPhotos_1" = "%@ photo deselected";
|
||||
"Attachment.DeselectedPhotos_2" = "%@ photos deselected";
|
||||
"Attachment.DeselectedPhotos_3_10" = "%@ photos deselected";
|
||||
"Attachment.DeselectedPhotos_any" = "%@ photos deselected";
|
||||
"Attachment.DeselectedPhotos_many" = "%@ photos deselected";
|
||||
"Attachment.DeselectedPhotos_0" = "%@ photos deselected";
|
||||
|
||||
"Attachment.DeselectedVideos_1" = "%@ video deselected";
|
||||
"Attachment.DeselectedVideos_2" = "%@ videos deselected";
|
||||
"Attachment.DeselectedVideos_3_10" = "%@ videos deselected";
|
||||
"Attachment.DeselectedVideos_any" = "%@ videos deselected";
|
||||
"Attachment.DeselectedVideos_many" = "%@ videos deselected";
|
||||
"Attachment.DeselectedVideos_0" = "%@ videos deselected";
|
||||
|
||||
"Attachment.DeselectedItems_1" = "%@ item deselected";
|
||||
"Attachment.DeselectedItems_2" = "%@ items deselected";
|
||||
"Attachment.DeselectedItems_3_10" = "%@ items deselected";
|
||||
"Attachment.DeselectedItems_any" = "%@ items deselected";
|
||||
"Attachment.DeselectedItems_many" = "%@ items deselected";
|
||||
"Attachment.DeselectedItems_0" = "%@ items deselected";
|
||||
|
||||
"PrivacyPhoneNumberSettings.CustomPublicLink" = "Users who have your number saved in their contacts will also see it on Telegram.\n\nThis public link opens a chat with you:\n[https://t.me/%@]()";
|
||||
|
||||
@ -7357,3 +7345,6 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Attachment.MyAlbums" = "My Albums";
|
||||
"Attachment.MediaTypes" = "Media Types";
|
||||
|
||||
"Attachment.LocationAccessTitle" = "Access Your Location";
|
||||
"Attachment.LocationAccessText" = "Share places or your live location.";
|
||||
|
@ -22,7 +22,7 @@ public enum AttachmentButtonType: Equatable {
|
||||
|
||||
public protocol AttachmentContainable: ViewController {
|
||||
var requestAttachmentMenuExpansion: () -> Void { get set }
|
||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void { get set }
|
||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void { get set }
|
||||
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void { get set }
|
||||
var cancelPanGesture: () -> Void { get set }
|
||||
|
||||
@ -289,7 +289,6 @@ public class AttachmentController: ViewController {
|
||||
self.currentType = type
|
||||
self.controller?.requestController(type, { [weak self] controller, mediaPickerContext in
|
||||
if let strongSelf = self {
|
||||
strongSelf.mediaPickerContext = mediaPickerContext
|
||||
if let controller = controller {
|
||||
strongSelf.controller?._ready.set(controller.ready.get())
|
||||
controller._presentedInModal = true
|
||||
@ -299,7 +298,9 @@ public class AttachmentController: ViewController {
|
||||
}
|
||||
controller.updateNavigationStack = { [weak self] f in
|
||||
if let strongSelf = self {
|
||||
strongSelf.currentControllers = f(strongSelf.currentControllers)
|
||||
let (controllers, mediaPickerContext) = f(strongSelf.currentControllers)
|
||||
strongSelf.currentControllers = controllers
|
||||
strongSelf.mediaPickerContext = mediaPickerContext
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
@ -346,6 +347,7 @@ public class AttachmentController: ViewController {
|
||||
strongSelf.switchingController = false
|
||||
}
|
||||
}
|
||||
strongSelf.mediaPickerContext = mediaPickerContext
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -419,7 +421,6 @@ public class AttachmentController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private var isCollapsed: Bool = false
|
||||
private var isUpdatingContainer = false
|
||||
private var switchingController = false
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
@ -427,22 +428,15 @@ public class AttachmentController: ViewController {
|
||||
|
||||
transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
if self.modalProgress < 0.5 {
|
||||
self.isCollapsed = false
|
||||
} else if self.modalProgress == 1.0 {
|
||||
self.isCollapsed = true
|
||||
}
|
||||
|
||||
var containerLayout = layout
|
||||
let containerRect: CGRect
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
let size = CGSize(width: 390.0, height: 620.0)
|
||||
|
||||
let insets = layout.insets(options: [.input])
|
||||
let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
|
||||
var position: CGPoint = CGPoint(x: masterWidth - 174.0, y: layout.size.height - size.height - layout.intrinsicInsets.bottom - 40.0)
|
||||
if let inputHeight = layout.inputHeight {
|
||||
position.y -= inputHeight
|
||||
}
|
||||
let position: CGPoint = CGPoint(x: masterWidth - 174.0, y: layout.size.height - size.height - insets.bottom - 40.0)
|
||||
|
||||
containerRect = CGRect(origin: position, size: size)
|
||||
containerLayout.size = containerRect.size
|
||||
containerLayout.intrinsicInsets.bottom = 12.0
|
||||
@ -469,8 +463,8 @@ public class AttachmentController: ViewController {
|
||||
}
|
||||
|
||||
|
||||
let isEffecitvelyCollapsedUpdated = (self.isCollapsed || self.selectionCount > 0) != (self.panel.isCollapsed || self.panel.isSelecting)
|
||||
let panelHeight = self.panel.update(layout: containerLayout, buttons: self.controller?.buttons ?? [], isCollapsed: self.isCollapsed, isSelecting: self.selectionCount > 0, transition: transition)
|
||||
let isEffecitvelyCollapsedUpdated = (self.selectionCount > 0) != (self.panel.isSelecting)
|
||||
let panelHeight = self.panel.update(layout: containerLayout, buttons: self.controller?.buttons ?? [], isSelecting: self.selectionCount > 0, transition: transition)
|
||||
var panelTransition = transition
|
||||
if isEffecitvelyCollapsedUpdated {
|
||||
panelTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
|
@ -169,7 +169,6 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
private var buttons: [AttachmentButtonType] = []
|
||||
private var selectedIndex: Int = 0
|
||||
private(set) var isCollapsed: Bool = false
|
||||
private(set) var isSelecting: Bool = false
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
@ -383,7 +382,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
strongSelf.updateChatPresentationInterfaceState({ $0.updatedTheme(presentationData.theme) })
|
||||
|
||||
if let layout = strongSelf.validLayout {
|
||||
let _ = strongSelf.update(layout: layout, buttons: strongSelf.buttons, isCollapsed: strongSelf.isCollapsed, isSelecting: strongSelf.isSelecting, transition: .immediate)
|
||||
let _ = strongSelf.update(layout: layout, buttons: strongSelf.buttons, isSelecting: strongSelf.isSelecting, transition: .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -551,13 +550,10 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, buttons: [AttachmentButtonType], isCollapsed: Bool, isSelecting: Bool, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
func update(layout: ContainerViewLayout, buttons: [AttachmentButtonType], isSelecting: Bool, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
self.validLayout = layout
|
||||
self.buttons = buttons
|
||||
|
||||
let isCollapsedUpdated = self.isCollapsed != isCollapsed
|
||||
self.isCollapsed = isCollapsed
|
||||
|
||||
let isSelectingUpdated = self.isSelecting != isSelecting
|
||||
self.isSelecting = isSelecting
|
||||
|
||||
@ -605,7 +601,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
containerFrame = bounds
|
||||
}
|
||||
let containerBounds = CGRect(origin: CGPoint(), size: containerFrame.size)
|
||||
if isCollapsedUpdated || isSelectingUpdated {
|
||||
if isSelectingUpdated {
|
||||
containerTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
} else {
|
||||
containerTransition = transition
|
||||
@ -635,13 +631,9 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.backgroundNode.update(size: containerBounds.size, transition: transition)
|
||||
containerTransition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: UIScreenPixel)))
|
||||
|
||||
let _ = self.updateScrollLayoutIfNeeded(force: isCollapsedUpdated || isSelectingUpdated, transition: containerTransition)
|
||||
let _ = self.updateScrollLayoutIfNeeded(force: isSelectingUpdated, transition: containerTransition)
|
||||
|
||||
var buttonTransition: Transition = .immediate
|
||||
if isCollapsedUpdated {
|
||||
buttonTransition = .easeInOut(duration: 0.25)
|
||||
}
|
||||
self.updateViews(transition: buttonTransition)
|
||||
self.updateViews(transition: .immediate)
|
||||
|
||||
return containerFrame.height
|
||||
}
|
||||
|
@ -518,7 +518,7 @@ public final class ComposedPoll {
|
||||
|
||||
private class CreatePollControllerImpl: ItemListController, AttachmentContainable {
|
||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in }
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||
public var cancelPanGesture: () -> Void = { }
|
||||
}
|
||||
|
@ -1499,7 +1499,10 @@ public final class ContactListNode: ASDisplayNode {
|
||||
self.indexNode.update(size: indexNodeFrame.size, color: self.presentationData.theme.list.itemAccentColor, sections: indexSections, transition: transition)
|
||||
}
|
||||
|
||||
self.authorizationNode.updateLayout(size: layout.size, insets: insets, transition: transition)
|
||||
let permissionSize = CGSize(width: layout.size.width, height: layout.size.height - 160.0)
|
||||
var permissionInsets = insets
|
||||
permissionInsets.bottom += 100.0
|
||||
self.authorizationNode.updateLayout(size: permissionSize, insets: permissionInsets, transition: transition)
|
||||
transition.updateFrame(node: self.authorizationNode, frame: self.bounds)
|
||||
|
||||
if !hadValidLayout {
|
||||
|
@ -20,7 +20,6 @@ public enum DeviceAccessCameraSubject {
|
||||
case qrCode
|
||||
}
|
||||
|
||||
|
||||
public enum DeviceAccessMicrophoneSubject {
|
||||
case audio
|
||||
case video
|
||||
|
@ -127,6 +127,7 @@ public class ImageNode: ASDisplayNode {
|
||||
private var first = true
|
||||
private let enableEmpty: Bool
|
||||
public var enableAnimatedTransition: Bool
|
||||
public var animateFirstTransition = true
|
||||
|
||||
private let _contentReady = Promise<Bool>()
|
||||
private var didSetReady: Bool = false
|
||||
@ -166,7 +167,7 @@ public class ImageNode: ASDisplayNode {
|
||||
if strongSelf.first && next != nil {
|
||||
strongSelf.first = false
|
||||
animate = false
|
||||
if strongSelf.isNodeLoaded {
|
||||
if strongSelf.isNodeLoaded && strongSelf.animateFirstTransition {
|
||||
strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
|
||||
}
|
||||
}
|
||||
|
@ -1056,7 +1056,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
var rightTitleInset: CGFloat = rightInset + 1.0
|
||||
if self.backButtonNode.supernode != nil {
|
||||
let backButtonSize = self.backButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape)
|
||||
leftTitleInset += backButtonSize.width + backButtonInset + 1.0
|
||||
leftTitleInset = backButtonSize.width + backButtonInset + 1.0
|
||||
|
||||
let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5
|
||||
self.backButtonNode.hitTestSlop = UIEdgeInsets(top: -topHitTestSlop, left: -27.0, bottom: -topHitTestSlop, right: -8.0)
|
||||
|
@ -177,7 +177,9 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode {
|
||||
|
||||
self.bottomStripeNode = ASDisplayNode()
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
self.maskNode.isUserInteractionEnabled = false
|
||||
|
||||
self.extractedBackgroundImageNode = ASImageNode()
|
||||
self.extractedBackgroundImageNode.displaysAsynchronously = false
|
||||
@ -671,7 +673,7 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.topStripeNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.addSubnode(strongSelf.bottomStripeNode)
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
|
||||
}
|
||||
if strongSelf.maskNode.supernode != nil {
|
||||
strongSelf.maskNode.removeFromSupernode()
|
||||
@ -693,10 +695,10 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.addSubnode(strongSelf.bottomStripeNode)
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||
}
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.addSubnode(strongSelf.maskNode)
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
@ -765,6 +767,8 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode {
|
||||
shimmerNode = ShimmerEffectNode()
|
||||
strongSelf.placeholderNode = shimmerNode
|
||||
if strongSelf.bottomStripeNode.supernode != nil {
|
||||
strongSelf.bottomStripeNode.removeFromSupernode()
|
||||
strongSelf.addSubnode(strongSelf.bottomStripeNode)
|
||||
strongSelf.insertSubnode(shimmerNode, belowSubnode: strongSelf.bottomStripeNode)
|
||||
} else {
|
||||
strongSelf.addSubnode(shimmerNode)
|
||||
|
@ -81,9 +81,11 @@ final class JoinLinkPreviewPeerContentNode: ASDisplayNode, ShareContentContainer
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, content: JoinLinkPreviewPeerContentNode.Content) {
|
||||
self.avatarNode = AvatarNode(font: avatarFont)
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.textAlignment = .center
|
||||
self.countNode = ASTextNode()
|
||||
self.aboutNode = ASTextNode()
|
||||
self.aboutNode.maximumNumberOfLines = 8
|
||||
self.aboutNode.textAlignment = .center
|
||||
self.descriptionNode = ASTextNode()
|
||||
self.descriptionNode.maximumNumberOfLines = 3
|
||||
self.descriptionNode.textAlignment = .center
|
||||
@ -153,7 +155,7 @@ final class JoinLinkPreviewPeerContentNode: ASDisplayNode, ShareContentContainer
|
||||
|
||||
if case let .request(isGroup, _, _, about, _) = content {
|
||||
if let about = about, !about.isEmpty {
|
||||
self.aboutNode.attributedText = NSAttributedString(string: about, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor)
|
||||
self.aboutNode.attributedText = NSAttributedString(string: about, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center)
|
||||
self.addSubnode(self.aboutNode)
|
||||
}
|
||||
|
||||
|
@ -406,7 +406,7 @@ open class LegacyController: ViewController, PresentableController, AttachmentCo
|
||||
public var disposables = DisposableSet()
|
||||
|
||||
open var requestAttachmentMenuExpansion: () -> Void = {}
|
||||
open var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in }
|
||||
open var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||
open var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||
open var cancelPanGesture: () -> Void = { }
|
||||
|
||||
|
@ -900,7 +900,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
}
|
||||
|
||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: leftInset + leftOffset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset - leftOffset, height: UIScreenPixel)))
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel))
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - UIScreenPixel), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel - nodeLayout.insets.bottom))
|
||||
|
||||
if let backgroundNode = strongSelf.backgroundNode {
|
||||
backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top), size: CGSize(width: params.width, height: nodeLayout.size.height - nodeLayout.insets.bottom))
|
||||
|
@ -43,6 +43,8 @@ swift_library(
|
||||
"//submodules/TooltipUI:TooltipUI",
|
||||
"//submodules/UndoUI:UndoUI",
|
||||
"//submodules/AttachmentUI:AttachmentUI",
|
||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -72,7 +72,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab
|
||||
private var interaction: LocationPickerInteraction?
|
||||
|
||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in }
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||
public var cancelPanGesture: () -> Void = { }
|
||||
|
||||
@ -294,7 +294,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab
|
||||
return
|
||||
}
|
||||
|
||||
self.displayNode = LocationPickerControllerNode(context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction, locationManager: self.locationManager)
|
||||
self.displayNode = LocationPickerControllerNode(controller: self, context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction, locationManager: self.locationManager)
|
||||
self.displayNodeDidLoad()
|
||||
self.controllerNode.beganInteractiveDragging = { [weak self] in
|
||||
self?.requestAttachmentMenuExpansion()
|
||||
@ -347,8 +347,4 @@ public final class LocationPickerController: ViewController, AttachmentContainab
|
||||
self.interaction?.dismissSearch()
|
||||
self.scrollToTop?()
|
||||
}
|
||||
|
||||
public func prepareForReuse() {
|
||||
self.updateTabBarAlpha(1.0, .animated(duration: 0.25, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +240,44 @@ struct LocationPickerState {
|
||||
}
|
||||
}
|
||||
|
||||
private class LocationContext: NSObject, CLLocationManagerDelegate {
|
||||
private let locationManager: CLLocationManager
|
||||
|
||||
private let accessSink = ValuePipe<CLAuthorizationStatus>()
|
||||
|
||||
override init() {
|
||||
self.locationManager = CLLocationManager()
|
||||
|
||||
super.init()
|
||||
|
||||
self.locationManager.delegate = self
|
||||
}
|
||||
|
||||
func locationAccess() -> Signal<CLAuthorizationStatus, NoError> {
|
||||
let initialStatus: CLAuthorizationStatus
|
||||
if #available(iOS 14.0, *) {
|
||||
initialStatus = self.locationManager.authorizationStatus
|
||||
} else {
|
||||
initialStatus = CLLocationManager.authorizationStatus()
|
||||
}
|
||||
return .single(initialStatus)
|
||||
|> then(
|
||||
self.accessSink.signal()
|
||||
)
|
||||
}
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
||||
self.accessSink.putNext(manager.authorizationStatus)
|
||||
}
|
||||
|
||||
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
|
||||
self.accessSink.putNext(status)
|
||||
}
|
||||
}
|
||||
|
||||
final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationManagerDelegate {
|
||||
private weak var controller: LocationPickerController?
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private let presentationDataPromise: Promise<PresentationData>
|
||||
@ -248,6 +285,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
||||
private let interaction: LocationPickerInteraction
|
||||
private let locationManager: LocationManager
|
||||
|
||||
private let locationContext: LocationContext
|
||||
|
||||
private let listNode: ListView
|
||||
private let emptyResultsTextNode: ImmediateTextNode
|
||||
private let headerNode: LocationMapHeaderNode
|
||||
@ -257,6 +296,10 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
||||
private let optionsNode: LocationOptionsNode
|
||||
private(set) var searchContainerNode: LocationSearchContainerNode?
|
||||
|
||||
private var placeholderBackgroundNode: NavigationBackgroundNode?
|
||||
private var placeholderNode: LocationPlaceholderNode?
|
||||
private var locationAccessDenied = false
|
||||
|
||||
private var enqueuedTransitions: [LocationPickerTransaction] = []
|
||||
|
||||
private var disposable: Disposable?
|
||||
@ -271,7 +314,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
||||
|
||||
var beganInteractiveDragging: () -> Void = {}
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction, locationManager: LocationManager) {
|
||||
init(controller: LocationPickerController, context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction, locationManager: LocationManager) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.presentationDataPromise = Promise(presentationData)
|
||||
@ -279,6 +323,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
||||
self.interaction = interaction
|
||||
self.locationManager = locationManager
|
||||
|
||||
self.locationContext = LocationContext()
|
||||
|
||||
self.state = LocationPickerState()
|
||||
self.statePromise = Promise(self.state)
|
||||
|
||||
@ -448,8 +494,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
||||
let previousAnnotations = Atomic<[LocationPinAnnotation]>(value: [])
|
||||
let previousEntries = Atomic<[LocationPickerEntry]?>(value: nil)
|
||||
|
||||
self.disposable = (combineLatest(self.presentationDataPromise.get(), self.statePromise.get(), userLocation, venues, foundVenues)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, state, userLocation, venues, foundVenuesAndLocation in
|
||||
self.disposable = (combineLatest(self.presentationDataPromise.get(), self.statePromise.get(), userLocation, venues, foundVenues, self.locationContext.locationAccess())
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, state, userLocation, venues, foundVenuesAndLocation, access in
|
||||
if let strongSelf = self {
|
||||
let (foundVenues, foundVenuesLocation) = foundVenuesAndLocation ?? (nil, nil)
|
||||
|
||||
@ -590,6 +636,18 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
||||
var updateLayout = false
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.45, curve: .spring)
|
||||
|
||||
if [.denied, .restricted].contains(access) {
|
||||
if !strongSelf.locationAccessDenied {
|
||||
strongSelf.locationAccessDenied = true
|
||||
updateLayout = true
|
||||
}
|
||||
} else {
|
||||
if strongSelf.locationAccessDenied {
|
||||
strongSelf.locationAccessDenied = false
|
||||
updateLayout = true
|
||||
}
|
||||
}
|
||||
|
||||
if previousState.displayingMapModeOptions != state.displayingMapModeOptions {
|
||||
updateLayout = true
|
||||
} else if previousState.selectedLocation.isCustom != state.selectedLocation.isCustom {
|
||||
@ -892,6 +950,57 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
||||
searchContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
searchContainerNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: nil, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), navigationBarHeight: navigationHeight, transition: transition)
|
||||
}
|
||||
|
||||
if self.locationAccessDenied {
|
||||
self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate)
|
||||
Queue.mainQueue().after(0.25) {
|
||||
self.controller?.updateTabBarAlpha(0.0, .immediate)
|
||||
}
|
||||
|
||||
var placeholderTransition = transition
|
||||
let placeholderNode: LocationPlaceholderNode
|
||||
let backgroundNode: NavigationBackgroundNode
|
||||
if let current = self.placeholderNode, let background = self.placeholderBackgroundNode {
|
||||
placeholderNode = current
|
||||
backgroundNode = background
|
||||
|
||||
backgroundNode.updateColor(color: self.presentationData.theme.rootController.tabBar.backgroundColor, transition: .immediate)
|
||||
} else {
|
||||
backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.tabBar.backgroundColor)
|
||||
if let navigationBar = self.controller?.navigationBar {
|
||||
self.insertSubnode(backgroundNode, belowSubnode: navigationBar)
|
||||
} else {
|
||||
self.addSubnode(backgroundNode)
|
||||
}
|
||||
self.placeholderBackgroundNode = backgroundNode
|
||||
|
||||
placeholderNode = LocationPlaceholderNode(content: .intro)
|
||||
placeholderNode.settingsPressed = { [weak self] in
|
||||
self?.context.sharedContext.applicationBindings.openSettings()
|
||||
}
|
||||
self.insertSubnode(placeholderNode, aboveSubnode: backgroundNode)
|
||||
self.placeholderNode = placeholderNode
|
||||
|
||||
placeholderTransition = .immediate
|
||||
}
|
||||
placeholderNode.update(layout: layout, theme: self.presentationData.theme, strings: self.presentationData.strings, transition: placeholderTransition)
|
||||
placeholderTransition.updateFrame(node: placeholderNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
let placeholderFrame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
backgroundNode.update(size: placeholderFrame.size, transition: placeholderTransition)
|
||||
placeholderTransition.updateFrame(node: placeholderNode, frame: placeholderFrame)
|
||||
} else {
|
||||
if let placeholderNode = self.placeholderNode {
|
||||
self.placeholderNode = nil
|
||||
placeholderNode.removeFromSupernode()
|
||||
}
|
||||
if let placeholderBackgroundNode = self.placeholderBackgroundNode {
|
||||
self.placeholderBackgroundNode = nil
|
||||
placeholderBackgroundNode.removeFromSupernode()
|
||||
}
|
||||
self.controller?.updateTabBarAlpha(1.0, .immediate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func updateSendActionHighlight(_ highlighted: Bool) {
|
||||
|
134
submodules/LocationUI/Sources/LocationPlaceholderNode.swift
Normal file
134
submodules/LocationUI/Sources/LocationPlaceholderNode.swift
Normal file
@ -0,0 +1,134 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import SolidRoundedButtonNode
|
||||
import PresentationDataUtils
|
||||
|
||||
final class LocationPlaceholderNode: ASDisplayNode {
|
||||
enum Content {
|
||||
case intro
|
||||
}
|
||||
|
||||
private let content: Content
|
||||
|
||||
private var animationNode: AnimatedStickerNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let buttonNode: SolidRoundedButtonNode
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private var cameraTextNode: ImmediateTextNode
|
||||
|
||||
var settingsPressed: () -> Void = {}
|
||||
var cameraPressed: () -> Void = {}
|
||||
|
||||
init(content: Content) {
|
||||
self.content = content
|
||||
|
||||
let name: String
|
||||
let playbackMode: AnimatedStickerPlaybackMode
|
||||
switch content {
|
||||
case .intro:
|
||||
name = "Location"
|
||||
playbackMode = .loop
|
||||
}
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 320, height: 320, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
self.titleNode.textAlignment = .center
|
||||
self.titleNode.maximumNumberOfLines = 1
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.lineSpacing = 0.1
|
||||
self.textNode.textAlignment = .center
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
||||
self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: .black, foregroundColor: .white), height: 50.0, cornerRadius: 12.0, gloss: true)
|
||||
|
||||
self.cameraTextNode = ImmediateTextNode()
|
||||
self.cameraTextNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.animationNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
if case .intro = self.content {
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
self.buttonNode.pressed = { [weak self] in
|
||||
self?.settingsPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
func update(layout: ContainerViewLayout, theme: PresentationTheme, strings: PresentationStrings, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
let themeUpdated = self.theme != theme
|
||||
self.theme = theme
|
||||
|
||||
var imageSize = CGSize(width: 144.0, height: 144.0)
|
||||
var insets = layout.insets(options: [])
|
||||
if layout.size.width == 460.0 {
|
||||
insets.top += -60.0
|
||||
imageSize = CGSize(width: 112.0, height: 112.0)
|
||||
} else {
|
||||
insets.top += -160.0
|
||||
}
|
||||
|
||||
let imageSpacing: CGFloat = 12.0
|
||||
let textSpacing: CGFloat = 12.0
|
||||
let buttonSpacing: CGFloat = 15.0
|
||||
let cameraSpacing: CGFloat = 13.0
|
||||
|
||||
let imageHeight = layout.size.width < layout.size.height ? imageSize.height + imageSpacing : 0.0
|
||||
|
||||
if themeUpdated {
|
||||
self.buttonNode.updateTheme(SolidRoundedButtonTheme(theme: theme))
|
||||
}
|
||||
self.buttonNode.title = strings.Attachment_OpenSettings
|
||||
let buttonWidth: CGFloat = 248.0
|
||||
let buttonHeight = self.buttonNode.updateLayout(width: buttonWidth, transition: transition)
|
||||
|
||||
let title: String
|
||||
let text: String
|
||||
switch self.content {
|
||||
case .intro:
|
||||
title = strings.Attachment_LocationAccessTitle
|
||||
text = strings.Attachment_LocationAccessText
|
||||
}
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(17.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .center)
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center)
|
||||
self.cameraTextNode.attributedText = NSAttributedString(string: strings.Attachment_OpenCamera, font: Font.regular(17.0), textColor: theme.list.itemAccentColor, paragraphAlignment: .center)
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - 40.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - 40.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
|
||||
let cameraSize = self.cameraTextNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - 70.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
|
||||
|
||||
let totalHeight = imageHeight + titleSize.height + textSpacing + textSize.height + buttonSpacing + buttonHeight + cameraSpacing + cameraSize.height
|
||||
let topOffset = insets.top + floor((layout.size.height - insets.top - insets.bottom - totalHeight) / 2.0)
|
||||
|
||||
transition.updateAlpha(node: self.animationNode, alpha: imageHeight > 0.0 ? 1.0 : 0.0)
|
||||
transition.updateFrame(node: self.animationNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - imageSize.width) / 2.0), y: topOffset), size: imageSize))
|
||||
self.animationNode.updateLayout(size: imageSize)
|
||||
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((layout.size.width - titleSize.width - layout.safeInsets.left - layout.safeInsets.right) / 2.0), y: topOffset + imageHeight), size: titleSize))
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((layout.size.width - textSize.width - layout.safeInsets.left - layout.safeInsets.right) / 2.0), y: self.titleNode.frame.maxY + textSpacing), size: textSize))
|
||||
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((layout.size.width - buttonWidth - layout.safeInsets.left - layout.safeInsets.right) / 2.0), y: self.textNode.frame.maxY + buttonSpacing), size: CGSize(width: buttonWidth, height: buttonHeight)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ enum LegacyMediaPickerGallerySource {
|
||||
case selection(item: TGMediaSelectableItem)
|
||||
}
|
||||
|
||||
func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, chatLocation: ChatLocation?, presentationData: PresentationData, source: LegacyMediaPickerGallerySource, immediateThumbnail: UIImage?, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, hasSilentPosting: Bool, hasSchedule: Bool, hasTimer: Bool, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (String) -> UIView?, completed: @escaping (TGMediaSelectableItem & TGMediaEditableItem, Bool, Int32?) -> Void, presentStickers: ((@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?)?, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: @escaping (ViewController, Any?) -> Void, finishedTransitionIn: @escaping () -> Void) {
|
||||
func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, chatLocation: ChatLocation?, presentationData: PresentationData, source: LegacyMediaPickerGallerySource, immediateThumbnail: UIImage?, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, hasSilentPosting: Bool, hasSchedule: Bool, hasTimer: Bool, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (String) -> UIView?, completed: @escaping (TGMediaSelectableItem & TGMediaEditableItem, Bool, Int32?) -> Void, presentStickers: ((@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?)?, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: @escaping (ViewController, Any?) -> Void, finishedTransitionIn: @escaping () -> Void, dismissAll: @escaping () -> Void) {
|
||||
let reminder = peer?.id == context.account.peerId
|
||||
|
||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
|
||||
@ -186,10 +186,12 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?,
|
||||
updateHiddenMedia(nil)
|
||||
legacyController?.dismiss()
|
||||
}
|
||||
|
||||
model.interfaceView.donePressed = { [weak controller] item in
|
||||
if let item = item as? TGMediaPickerGalleryItem {
|
||||
controller?.dismissWhenReady(animated: true)
|
||||
completed(item.asset, false, nil)
|
||||
dismissAll()
|
||||
}
|
||||
}
|
||||
model.interfaceView.doneLongPressed = { [weak selectionContext, weak editingContext, weak legacyController, weak model] item in
|
||||
|
@ -120,6 +120,8 @@ private final class MediaGroupsGridAlbumItemNode : ListViewItemNode {
|
||||
self.imageNode = ImageNode()
|
||||
self.imageNode.clipsToBounds = true
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 62.0, height: 62.0))
|
||||
self.imageNode.contentMode = .scaleAspectFill
|
||||
self.imageNode.animateFirstTransition = false
|
||||
|
||||
self.titleNode = TextNode()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
@ -332,13 +334,19 @@ private class MediaGroupsAlbumGridItemNode: ListViewItemNode {
|
||||
let listInsets = UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0)
|
||||
strongSelf.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: contentSize.height, height: contentSize.width - params.leftInset - params.rightInset)
|
||||
strongSelf.listNode.position = CGPoint(x: contentSize.width / 2.0, y: contentSize.height / 2.0)
|
||||
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: contentSize.height, height: contentSize.width), insets: listInsets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: contentSize.height, height: contentSize.width - params.leftInset - params.rightInset), insets: listInsets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
|
||||
var entries: [MediaGroupsGridAlbumEntry] = []
|
||||
var index: Int = 0
|
||||
for collection in item.collections {
|
||||
let result = PHAsset.fetchAssets(in: collection, options: nil)
|
||||
if let firstItem = result.firstObject {
|
||||
let firstItem: PHAsset?
|
||||
if collection.assetCollectionSubtype == .smartAlbumUserLibrary {
|
||||
firstItem = result.lastObject
|
||||
} else {
|
||||
firstItem = result.firstObject
|
||||
}
|
||||
if let firstItem = firstItem {
|
||||
entries.append(MediaGroupsGridAlbumEntry(theme: item.presentationData.theme, index: index, collection: collection, firstItem: firstItem, count: presentationStringsFormattedNumber(Int32(result.count))))
|
||||
index += 1
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
||||
self.imageNode.clipsToBounds = true
|
||||
self.imageNode.contentMode = .scaleAspectFill
|
||||
self.imageNode.isLayerBacked = false
|
||||
self.imageNode.animateFirstTransition = false
|
||||
|
||||
self.gradientNode = ASImageNode()
|
||||
self.gradientNode.displaysAsynchronously = false
|
||||
|
@ -18,6 +18,8 @@ import AttachmentUI
|
||||
import ContextUI
|
||||
import WebSearchUI
|
||||
|
||||
let overflowInset: CGFloat = 70.0
|
||||
|
||||
final class MediaPickerInteraction {
|
||||
let openMedia: (PHFetchResult<PHAsset>, Int, UIImage?) -> Void
|
||||
let openSelectedMedia: (TGMediaSelectableItem, UIImage?) -> Void
|
||||
@ -99,10 +101,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
public var legacyCompletion: (_ signals: [Any], _ silently: Bool, _ scheduleTime: Int32?) -> Void = { _, _, _ in }
|
||||
|
||||
public var requestAttachmentMenuExpansion: () -> Void = { }
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in }
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||
public var cancelPanGesture: () -> Void = { }
|
||||
|
||||
var dismissAll: () -> Void = { }
|
||||
|
||||
private class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
|
||||
enum DisplayMode {
|
||||
case all
|
||||
@ -166,6 +170,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
super.init()
|
||||
|
||||
if controller.collection != nil {
|
||||
self.preloadPromise.set(false)
|
||||
}
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
self.containerNode.addSubnode(self.backgroundNode)
|
||||
self.containerNode.addSubnode(self.gridNode)
|
||||
@ -281,6 +289,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
||||
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
||||
|
||||
if self.controller?.collection != nil {
|
||||
self.gridNode.view.interactiveTransitionGestureRecognizerTest = { point -> Bool in
|
||||
return point.x > 44.0 + overflowInset
|
||||
}
|
||||
}
|
||||
|
||||
if self.controller?.collection == nil {
|
||||
let cameraView = TGAttachmentCameraView(forSelfPortrait: false)!
|
||||
cameraView.clipsToBounds = true
|
||||
@ -315,6 +329,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil)
|
||||
}
|
||||
}
|
||||
if self.controller?.collection != nil {
|
||||
self.selectionGesture?.sideInset = 44.0 + overflowInset
|
||||
}
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
@ -481,8 +498,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
}, presentStickers: controller.presentStickers, presentSchedulePicker: controller.presentSchedulePicker, presentTimerPicker: controller.presentTimerPicker, getCaptionPanelView: controller.getCaptionPanelView, present: { [weak self] c, a in
|
||||
self?.controller?.present(c, in: .window(.root), with: a)
|
||||
}, finishedTransitionIn: {
|
||||
self.openingMedia = false
|
||||
}, finishedTransitionIn: { [weak self] in
|
||||
self?.openingMedia = false
|
||||
}, dismissAll: { [weak self] in
|
||||
self?.controller?.dismissAll()
|
||||
})
|
||||
}
|
||||
|
||||
@ -512,8 +531,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
}, presentStickers: controller.presentStickers, presentSchedulePicker: controller.presentSchedulePicker, presentTimerPicker: controller.presentTimerPicker, getCaptionPanelView: controller.getCaptionPanelView, present: { [weak self] c, a in
|
||||
self?.controller?.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
}, finishedTransitionIn: {
|
||||
self.openingMedia = false
|
||||
}, finishedTransitionIn: { [weak self] in
|
||||
self?.openingMedia = false
|
||||
}, dismissAll: { [weak self] in
|
||||
self?.controller?.dismissAll()
|
||||
})
|
||||
}
|
||||
|
||||
@ -611,13 +632,20 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
private var previousContentOffset: GridNodeVisibleContentOffset?
|
||||
|
||||
func updateNavigation(transition: ContainedViewLayoutTransition) {
|
||||
func updateNavigation(delayDisappear: Bool = false, transition: ContainedViewLayoutTransition) {
|
||||
if let selectionNode = self.selectionNode, selectionNode.alpha > 0.0 {
|
||||
self.controller?.navigationBar?.updateBackgroundAlpha(1.0, transition: .immediate)
|
||||
self.controller?.updateTabBarAlpha(1.0, transition)
|
||||
} else if self.placeholderNode != nil {
|
||||
self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate)
|
||||
self.controller?.updateTabBarAlpha(0.0, transition)
|
||||
|
||||
if delayDisappear {
|
||||
Queue.mainQueue().after(0.25) {
|
||||
self.controller?.updateTabBarAlpha(0.0, transition)
|
||||
}
|
||||
} else {
|
||||
self.controller?.updateTabBarAlpha(0.0, transition)
|
||||
}
|
||||
} else {
|
||||
var previousContentOffsetValue: CGFloat?
|
||||
if let previousContentOffset = self.previousContentOffset, case let .known(value) = previousContentOffset {
|
||||
@ -637,6 +665,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
case .unknown, .none:
|
||||
self.controller?.navigationBar?.updateBackgroundAlpha(1.0, transition: .immediate)
|
||||
}
|
||||
self.controller?.updateTabBarAlpha(1.0, transition)
|
||||
}
|
||||
|
||||
let count = Int32(self.controller?.interaction?.selectionState?.count() ?? 0)
|
||||
@ -652,7 +681,6 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
var insets = layout.insets(options: [])
|
||||
insets.top += navigationBarHeight
|
||||
|
||||
let overflowInset: CGFloat = 70.0
|
||||
let bounds = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: layout.size.height))
|
||||
let innerBounds = CGRect(origin: CGPoint(x: -overflowInset, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))
|
||||
|
||||
@ -968,6 +996,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
}, selectionState: selectionContext ?? TGMediaSelectionContext(), editingState: editingContext ?? TGMediaEditingContext())
|
||||
self.interaction?.selectionState?.grouping = true
|
||||
|
||||
self.updateSelectionState(count: Int32(selectionContext?.count() ?? 0))
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@ -1014,7 +1044,11 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
@objc private func backPressed() {
|
||||
self.updateNavigationStack { current in
|
||||
return current.filter { $0 !== self }
|
||||
var mediaPickerContext: AttachmentMediaPickerContext?
|
||||
if let first = current.first as? MediaPickerScreen {
|
||||
mediaPickerContext = first.webSearchController?.mediaPickerContext ?? first.mediaPickerContext
|
||||
}
|
||||
return (current.filter { $0 !== self }, mediaPickerContext)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1039,9 +1073,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
public func prepareForReuse() {
|
||||
self.controllerNode.cameraView?.resumePreview()
|
||||
|
||||
Queue.mainQueue().after(0.2, {
|
||||
self.controllerNode.updateNavigation(transition: .animated(duration: 0.15, curve: .easeInOut))
|
||||
})
|
||||
self.controllerNode.updateNavigation(delayDisappear: true, transition: .immediate)
|
||||
}
|
||||
|
||||
@objc private func searchOrMorePressed(node: ContextReferenceContentNode, gesture: ContextGesture?) {
|
||||
@ -1051,20 +1083,31 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.presentWebSearch(MediaGroupsScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mediaAssetsContext: self.controllerNode.mediaAssetsContext, openGroup: { [weak self] collection in
|
||||
if let strongSelf = self {
|
||||
if let webSearchController = strongSelf.webSearchController {
|
||||
strongSelf.webSearchController = nil
|
||||
if collection.assetCollectionSubtype != .smartAlbumUserLibrary {
|
||||
Queue.mainQueue().after(0.5) {
|
||||
webSearchController.cancel()
|
||||
}
|
||||
// strongSelf.webSearchController = nil
|
||||
// Queue.mainQueue().after(0.5) {
|
||||
// webSearchController.cancel()
|
||||
// }
|
||||
} else {
|
||||
strongSelf.webSearchController = nil
|
||||
webSearchController.cancel()
|
||||
}
|
||||
}
|
||||
if collection.assetCollectionSubtype != .smartAlbumUserLibrary {
|
||||
let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, chatLocation: strongSelf.chatLocation, bannedSendMedia: strongSelf.bannedSendMedia, collection: collection, editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState)
|
||||
|
||||
mediaPicker.presentStickers = strongSelf.presentStickers
|
||||
mediaPicker.presentSchedulePicker = strongSelf.presentSchedulePicker
|
||||
mediaPicker.presentTimerPicker = strongSelf.presentTimerPicker
|
||||
mediaPicker.getCaptionPanelView = strongSelf.getCaptionPanelView
|
||||
mediaPicker.legacyCompletion = strongSelf.legacyCompletion
|
||||
mediaPicker.dismissAll = { [weak self] in
|
||||
self?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
mediaPicker._presentedInModal = true
|
||||
mediaPicker.updateNavigationStack = strongSelf.updateNavigationStack
|
||||
strongSelf.updateNavigationStack({ _ in return [strongSelf, mediaPicker]})
|
||||
strongSelf.updateNavigationStack({ _ in return ([strongSelf, mediaPicker], strongSelf.mediaPickerContext)})
|
||||
}
|
||||
}
|
||||
}))
|
||||
@ -1205,6 +1248,8 @@ private class MediaPickerGridSelectionGesture: UIPanGestureRecognizer {
|
||||
|
||||
private var initialLocation: CGPoint?
|
||||
|
||||
var sideInset: CGFloat = 0.0
|
||||
|
||||
init(target: Any?, action: Selector?, gridNode: GridNode) {
|
||||
self.gridNode = gridNode
|
||||
|
||||
@ -1216,12 +1261,17 @@ private class MediaPickerGridSelectionGesture: UIPanGestureRecognizer {
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
|
||||
guard let touch = touches.first, let gridNode = self.gridNode else {
|
||||
guard let touch = touches.first, self.numberOfTouches == 1, let gridNode = self.gridNode else {
|
||||
return
|
||||
}
|
||||
|
||||
let location = touch.location(in: gridNode.view)
|
||||
self.initialLocation = location
|
||||
|
||||
if location.x > self.sideInset {
|
||||
self.initialLocation = location
|
||||
} else {
|
||||
self.state = .failed
|
||||
}
|
||||
}
|
||||
|
||||
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
|
@ -189,7 +189,7 @@ public final class PermissionContentNode: ASDisplayNode {
|
||||
public func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets)
|
||||
|
||||
let sidePadding: CGFloat
|
||||
var sidePadding: CGFloat
|
||||
let fontSize: CGFloat
|
||||
if min(size.width, size.height) > 330.0 {
|
||||
fontSize = 24.0
|
||||
@ -198,8 +198,9 @@ public final class PermissionContentNode: ASDisplayNode {
|
||||
fontSize = 20.0
|
||||
sidePadding = 20.0
|
||||
}
|
||||
sidePadding += insets.left
|
||||
|
||||
let smallerSidePadding: CGFloat = 20.0
|
||||
let smallerSidePadding: CGFloat = 20.0 + insets.left
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(fontSize), textColor: self.theme.list.itemPrimaryTextColor)
|
||||
|
||||
@ -207,7 +208,7 @@ public final class PermissionContentNode: ASDisplayNode {
|
||||
let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: size.width - smallerSidePadding * 2.0, height: .greatestFiniteMagnitude))
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: size.width - sidePadding * 2.0, height: .greatestFiniteMagnitude))
|
||||
let buttonInset: CGFloat = 16.0
|
||||
let buttonWidth = min(size.width, size.height) - buttonInset * 2.0
|
||||
let buttonWidth = min(size.width, size.height) - buttonInset * 2.0 - insets.left - insets.right
|
||||
let buttonHeight = self.actionButton.updateLayout(width: buttonWidth, transition: transition)
|
||||
let footerSize = self.footerNode.updateLayout(CGSize(width: size.width - smallerSidePadding * 2.0, height: .greatestFiniteMagnitude))
|
||||
let privacyButtonSize = self.privacyPolicyButton.measure(CGSize(width: size.width - sidePadding * 2.0, height: .greatestFiniteMagnitude))
|
||||
|
@ -165,10 +165,12 @@ private func attachmentFileControllerEntries(presentationData: PresentationData,
|
||||
|
||||
private class AttachmentFileControllerImpl: ItemListController, AttachmentContainable {
|
||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in }
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||
public var cancelPanGesture: () -> Void = { }
|
||||
|
||||
var delayDisappear = false
|
||||
|
||||
var resetForReuseImpl: () -> Void = {}
|
||||
public func resetForReuse() {
|
||||
self.resetForReuseImpl()
|
||||
@ -176,7 +178,9 @@ private class AttachmentFileControllerImpl: ItemListController, AttachmentContai
|
||||
}
|
||||
|
||||
public func prepareForReuse() {
|
||||
self.delayDisappear = true
|
||||
self.visibleBottomContentOffsetChanged?(self.visibleBottomContentOffset)
|
||||
self.delayDisappear = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,13 +304,21 @@ public func attachmentFileController(context: AccountContext, updatedPresentatio
|
||||
}
|
||||
|
||||
let controller = AttachmentFileControllerImpl(context: context, state: signal)
|
||||
controller.delayDisappear = true
|
||||
controller.visibleBottomContentOffsetChanged = { [weak controller] offset in
|
||||
switch offset {
|
||||
case let .known(value):
|
||||
let backgroundAlpha: CGFloat = min(30.0, value) / 30.0
|
||||
controller?.updateTabBarAlpha(backgroundAlpha, .immediate)
|
||||
let backgroundAlpha: CGFloat = min(30.0, max(0.0, value)) / 30.0
|
||||
if backgroundAlpha.isZero && controller?.delayDisappear == true {
|
||||
Queue.mainQueue().after(0.25, {
|
||||
controller?.updateTabBarAlpha(backgroundAlpha, .animated(duration: 0.1, curve: .easeInOut))
|
||||
})
|
||||
} else {
|
||||
controller?.updateTabBarAlpha(backgroundAlpha, .immediate)
|
||||
}
|
||||
case .unknown, .none:
|
||||
controller?.updateTabBarAlpha(1.0, .immediate)
|
||||
controller?.delayDisappear = false
|
||||
}
|
||||
}
|
||||
controller.resetForReuseImpl = {
|
||||
|
@ -76,7 +76,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
}
|
||||
|
||||
var requestAttachmentMenuExpansion: () -> Void = {}
|
||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in }
|
||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||
var cancelPanGesture: () -> Void = { }
|
||||
|
||||
@ -237,6 +237,8 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
}
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.updateTabBarAlpha(1.0, .immediate)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
@ -342,6 +344,10 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
public var mediaPickerContext: AttachmentMediaPickerContext {
|
||||
return ContactsPickerContext(controller: self)
|
||||
}
|
||||
|
||||
public func prepareForReuse() {
|
||||
self.updateTabBarAlpha(1.0, .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
private let searchBarFont = Font.regular(17.0)
|
||||
|
@ -496,7 +496,9 @@ class WebSearchControllerNode: ASDisplayNode {
|
||||
|
||||
insets.top += segmentedHeight
|
||||
insets.bottom += toolbarHeight
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: insets, preloadSize: 400.0, type: gridNodeLayoutForContainerLayout(layout)), transition: .immediate), itemTransition: .immediate, stationaryItems: .none,updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
|
||||
let gridInsets = UIEdgeInsets(top: insets.top, left: layout.safeInsets.left, bottom: insets.bottom, right: layout.safeInsets.right)
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: gridInsets, preloadSize: 400.0, type: gridNodeLayoutForContainerLayout(layout)), transition: .immediate), itemTransition: .immediate, stationaryItems: .none,updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user