Attachment menu improvements

This commit is contained in:
Ilya Laktyushin 2022-02-27 04:07:09 +04:00
parent 82104445dd
commit 7b8c90fbdc
17 changed files with 337 additions and 69 deletions

View File

@ -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.";

View File

@ -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
@ -346,6 +345,7 @@ public class AttachmentController: ViewController {
strongSelf.switchingController = false
}
}
strongSelf.mediaPickerContext = mediaPickerContext
}
})
}
@ -419,7 +419,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 +426,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 +461,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)

View File

@ -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
}

View File

@ -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 {

View File

@ -20,7 +20,6 @@ public enum DeviceAccessCameraSubject {
case qrCode
}
public enum DeviceAccessMicrophoneSubject {
case audio
case video

View File

@ -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)

View File

@ -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))

View File

@ -43,6 +43,8 @@ swift_library(
"//submodules/TooltipUI:TooltipUI",
"//submodules/UndoUI:UndoUI",
"//submodules/AttachmentUI:AttachmentUI",
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
],
visibility = [
"//visibility:public",

View File

@ -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()
@ -324,6 +324,8 @@ public final class LocationPickerController: ViewController, AttachmentContainab
})
self.navigationBar?.passthroughTouches = false
self.updateTabBarAlpha(1.0, .immediate)
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
@ -349,6 +351,6 @@ public final class LocationPickerController: ViewController, AttachmentContainab
}
public func prepareForReuse() {
self.updateTabBarAlpha(1.0, .animated(duration: 0.25, curve: .easeInOut))
self.updateTabBarAlpha(1.0, .immediate)
}
}

View File

@ -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 = true
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,49 @@ 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.2) {
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()
}
}
func updateSendActionHighlight(_ highlighted: Bool) {

View 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)))
}
}

View File

@ -120,6 +120,7 @@ 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.titleNode = TextNode()
self.titleNode.isUserInteractionEnabled = false
@ -332,13 +333,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
}

View File

@ -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
@ -281,6 +283,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 +323,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 +335,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
return false
}
}
fileprivate func dismissInput() {
self.view.window?.endEditing(true)
}
@ -611,13 +622,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.2) {
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 +655,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 +671,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))
@ -1039,9 +1057,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,12 +1067,13 @@ 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()
}
}
@ -1205,6 +1222,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 +1235,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) {

View File

@ -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))

View File

@ -169,6 +169,8 @@ private class AttachmentFileControllerImpl: ItemListController, AttachmentContai
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.2, {
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 = {

View File

@ -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)

View File

@ -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)