diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index a13b57cb77..8134bd8295 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -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."; diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index 001f493fa0..09fcf5f202 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -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) diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 5a9f15621e..aa587350eb 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -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,12 +550,9 @@ 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 } diff --git a/submodules/ComposePollUI/Sources/CreatePollController.swift b/submodules/ComposePollUI/Sources/CreatePollController.swift index cf94154189..a11dc5f509 100644 --- a/submodules/ComposePollUI/Sources/CreatePollController.swift +++ b/submodules/ComposePollUI/Sources/CreatePollController.swift @@ -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 = { } } diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 79e57cac6b..9ed4b3282a 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -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 { diff --git a/submodules/DeviceAccess/Sources/DeviceAccess.swift b/submodules/DeviceAccess/Sources/DeviceAccess.swift index 6f5642e2da..dd0382288d 100644 --- a/submodules/DeviceAccess/Sources/DeviceAccess.swift +++ b/submodules/DeviceAccess/Sources/DeviceAccess.swift @@ -20,7 +20,6 @@ public enum DeviceAccessCameraSubject { case qrCode } - public enum DeviceAccessMicrophoneSubject { case audio case video diff --git a/submodules/Display/Source/ImageNode.swift b/submodules/Display/Source/ImageNode.swift index bacfd28821..5b1eb093f0 100644 --- a/submodules/Display/Source/ImageNode.swift +++ b/submodules/Display/Source/ImageNode.swift @@ -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() 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) } } diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 9a83f867d7..940d5b0f62 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -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) diff --git a/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift b/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift index 23adfede45..f15d215be8 100644 --- a/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift @@ -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) diff --git a/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewPeerContentNode.swift b/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewPeerContentNode.swift index 341905959f..c671f9d62e 100644 --- a/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewPeerContentNode.swift +++ b/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewPeerContentNode.swift @@ -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) } diff --git a/submodules/LegacyUI/Sources/LegacyController.swift b/submodules/LegacyUI/Sources/LegacyController.swift index f8b851b42a..eb8ea5f944 100644 --- a/submodules/LegacyUI/Sources/LegacyController.swift +++ b/submodules/LegacyUI/Sources/LegacyController.swift @@ -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 = { } diff --git a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift index 9cfb50e01e..86caa402ea 100644 --- a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift @@ -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)) diff --git a/submodules/LocationUI/BUILD b/submodules/LocationUI/BUILD index eb788ecccd..4d8245d7ad 100644 --- a/submodules/LocationUI/BUILD +++ b/submodules/LocationUI/BUILD @@ -43,6 +43,8 @@ swift_library( "//submodules/TooltipUI:TooltipUI", "//submodules/UndoUI:UndoUI", "//submodules/AttachmentUI:AttachmentUI", + "//submodules/AnimatedStickerNode:AnimatedStickerNode", + "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", ], visibility = [ "//visibility:public", diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index ce9f7f7bb5..5dace1b7f1 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -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)) - } } diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index 3e22b6a739..65b39f4240 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -240,7 +240,44 @@ struct LocationPickerState { } } +private class LocationContext: NSObject, CLLocationManagerDelegate { + private let locationManager: CLLocationManager + + private let accessSink = ValuePipe() + + override init() { + self.locationManager = CLLocationManager() + + super.init() + + self.locationManager.delegate = self + } + + func locationAccess() -> Signal { + 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 @@ -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) @@ -589,6 +635,18 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM if let (layout, navigationBarHeight) = strongSelf.validLayout { 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 @@ -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) { diff --git a/submodules/LocationUI/Sources/LocationPlaceholderNode.swift b/submodules/LocationUI/Sources/LocationPlaceholderNode.swift new file mode 100644 index 0000000000..865d095b73 --- /dev/null +++ b/submodules/LocationUI/Sources/LocationPlaceholderNode.swift @@ -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))) + } +} + + diff --git a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift index 1b4b7058eb..11c67686bb 100644 --- a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift +++ b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift @@ -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) + completed(item.asset, false, nil) + dismissAll() } } model.interfaceView.doneLongPressed = { [weak selectionContext, weak editingContext, weak legacyController, weak model] item in diff --git a/submodules/MediaPickerUI/Sources/MediaGroupsAlbumGridItem.swift b/submodules/MediaPickerUI/Sources/MediaGroupsAlbumGridItem.swift index 6d63017e7e..0f7af1e723 100644 --- a/submodules/MediaPickerUI/Sources/MediaGroupsAlbumGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaGroupsAlbumGridItem.swift @@ -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 } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index 7eb425fbc5..a7b961d3f3 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -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 diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index a46ed9b0dd..cd61bf837e 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -18,6 +18,8 @@ import AttachmentUI import ContextUI import WebSearchUI +let overflowInset: CGFloat = 70.0 + final class MediaPickerInteraction { let openMedia: (PHFetchResult, 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 @@ -163,9 +167,13 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.tabBar.backgroundColor) self.backgroundNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor self.gridNode = GridNode() - + 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 { @@ -324,7 +341,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { return false } } - + fileprivate func dismissInput() { self.view.window?.endEditing(true) } @@ -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, 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, with event: UIEvent) { diff --git a/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift b/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift index 72c94adb4d..b9763d429e 100644 --- a/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift +++ b/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift @@ -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)) diff --git a/submodules/TelegramUI/Sources/AttachmentFileController.swift b/submodules/TelegramUI/Sources/AttachmentFileController.swift index a903ebcffe..dc7d2b2917 100644 --- a/submodules/TelegramUI/Sources/AttachmentFileController.swift +++ b/submodules/TelegramUI/Sources/AttachmentFileController.swift @@ -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 = { diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift index 801591f4d7..f805c0e89d 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionController.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift @@ -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) diff --git a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift index 39af94a084..da0ebac425 100644 --- a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift +++ b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift @@ -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)