Add option to request venues around arbitrary location

This commit is contained in:
Ilya Laktyushin 2019-12-10 21:17:01 +04:00
parent 05a3f563e9
commit 2e43d1e98f
4 changed files with 126 additions and 24 deletions

View File

@ -7,19 +7,16 @@ private let panelInset: CGFloat = 4.0
private let panelSize = CGSize(width: 46.0, height: 90.0) private let panelSize = CGSize(width: 46.0, height: 90.0)
private func generateBackgroundImage(theme: PresentationTheme) -> UIImage? { private func generateBackgroundImage(theme: PresentationTheme) -> UIImage? {
return generateImage(CGSize(width: panelSize.width + panelInset * 2.0, height: panelSize.height + panelInset * 2.0)) { size, context in let cornerRadius: CGFloat = 9.0
return generateImage(CGSize(width: (cornerRadius + panelInset) * 2.0, height: (cornerRadius + panelInset) * 2.0)) { size, context in
context.clear(CGRect(origin: CGPoint(), size: size)) context.clear(CGRect(origin: CGPoint(), size: size))
context.setShadow(offset: CGSize(), blur: 10.0, color: UIColor(rgb: 0x000000, alpha: 0.2).cgColor) context.setShadow(offset: CGSize(), blur: 10.0, color: UIColor(rgb: 0x000000, alpha: 0.2).cgColor)
context.setFillColor(theme.rootController.navigationBar.backgroundColor.cgColor) context.setFillColor(theme.rootController.navigationBar.backgroundColor.cgColor)
let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: panelInset, y: panelInset), size: panelSize), cornerRadius: 9.0) let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: panelInset, y: panelInset), size: CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0)), cornerRadius: cornerRadius)
context.addPath(path.cgPath) context.addPath(path.cgPath)
context.fillPath() context.fillPath()
}?.stretchableImage(withLeftCapWidth: Int(cornerRadius + panelInset), topCapHeight: Int(cornerRadius + panelInset))
context.setShadow(offset: CGSize(), blur: 0.0, color: nil)
context.setFillColor(theme.rootController.navigationBar.separatorColor.cgColor)
context.fill(CGRect(x: panelInset, y: panelInset + floorToScreenPixels(panelSize.height / 2.0), width: panelSize.width, height: UIScreenPixel))
}
} }
private func generateShadowImage(theme: PresentationTheme, highlighted: Bool) -> UIImage? { private func generateShadowImage(theme: PresentationTheme, highlighted: Bool) -> UIImage? {
@ -38,26 +35,39 @@ final class LocationMapHeaderNode: ASDisplayNode {
private var presentationData: PresentationData private var presentationData: PresentationData
private let toggleMapModeSelection: () -> Void private let toggleMapModeSelection: () -> Void
private let goToUserLocation: () -> Void private let goToUserLocation: () -> Void
private let showPlacesInThisArea: () -> Void
private var displayingPlacesButton = false
let mapNode: LocationMapNode let mapNode: LocationMapNode
private let optionsBackgroundNode: ASImageNode private let optionsBackgroundNode: ASImageNode
private let optionsSeparatorNode: ASDisplayNode
private let infoButtonNode: HighlightableButtonNode private let infoButtonNode: HighlightableButtonNode
private let locationButtonNode: HighlightableButtonNode private let locationButtonNode: HighlightableButtonNode
private let placesBackgroundNode: ASImageNode
private let placesButtonNode: HighlightableButtonNode
private let shadowNode: ASImageNode private let shadowNode: ASImageNode
init(presentationData: PresentationData, toggleMapModeSelection: @escaping () -> Void, goToUserLocation: @escaping () -> Void) { private var validLayout: (ContainerViewLayout, CGFloat, CGFloat, CGFloat, CGSize)?
init(presentationData: PresentationData, toggleMapModeSelection: @escaping () -> Void, goToUserLocation: @escaping () -> Void, showPlacesInThisArea: @escaping () -> Void = {}) {
self.presentationData = presentationData self.presentationData = presentationData
self.toggleMapModeSelection = toggleMapModeSelection self.toggleMapModeSelection = toggleMapModeSelection
self.goToUserLocation = goToUserLocation self.goToUserLocation = goToUserLocation
self.showPlacesInThisArea = showPlacesInThisArea
self.mapNode = LocationMapNode() self.mapNode = LocationMapNode()
self.optionsBackgroundNode = ASImageNode() self.optionsBackgroundNode = ASImageNode()
self.optionsBackgroundNode.contentMode = .scaleToFill
self.optionsBackgroundNode.displaysAsynchronously = false self.optionsBackgroundNode.displaysAsynchronously = false
self.optionsBackgroundNode.displayWithoutProcessing = true self.optionsBackgroundNode.displayWithoutProcessing = true
self.optionsBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme) self.optionsBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
self.optionsBackgroundNode.isUserInteractionEnabled = true self.optionsBackgroundNode.isUserInteractionEnabled = true
self.optionsSeparatorNode = ASDisplayNode()
self.optionsSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
self.infoButtonNode = HighlightableButtonNode() self.infoButtonNode = HighlightableButtonNode()
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal) self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected) self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
@ -66,6 +76,16 @@ final class LocationMapHeaderNode: ASDisplayNode {
self.locationButtonNode = HighlightableButtonNode() self.locationButtonNode = HighlightableButtonNode()
self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal) self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
self.placesBackgroundNode = ASImageNode()
self.placesBackgroundNode.contentMode = .scaleToFill
self.placesBackgroundNode.displaysAsynchronously = false
self.placesBackgroundNode.displayWithoutProcessing = true
self.placesBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
self.placesBackgroundNode.isUserInteractionEnabled = true
self.placesButtonNode = HighlightableButtonNode()
self.placesButtonNode.setTitle("Places In This Area", with: Font.regular(17.0), with: presentationData.theme.rootController.navigationBar.buttonColor, for: .normal)
self.shadowNode = ASImageNode() self.shadowNode = ASImageNode()
self.shadowNode.contentMode = .scaleToFill self.shadowNode.contentMode = .scaleToFill
self.shadowNode.displaysAsynchronously = false self.shadowNode.displaysAsynchronously = false
@ -78,42 +98,65 @@ final class LocationMapHeaderNode: ASDisplayNode {
self.addSubnode(self.mapNode) self.addSubnode(self.mapNode)
self.addSubnode(self.optionsBackgroundNode) self.addSubnode(self.optionsBackgroundNode)
self.optionsBackgroundNode.addSubnode(self.optionsSeparatorNode)
self.optionsBackgroundNode.addSubnode(self.infoButtonNode) self.optionsBackgroundNode.addSubnode(self.infoButtonNode)
self.optionsBackgroundNode.addSubnode(self.locationButtonNode) self.optionsBackgroundNode.addSubnode(self.locationButtonNode)
self.addSubnode(self.placesBackgroundNode)
self.placesBackgroundNode.addSubnode(self.placesButtonNode)
self.addSubnode(self.shadowNode) self.addSubnode(self.shadowNode)
self.infoButtonNode.addTarget(self, action: #selector(self.infoPressed), forControlEvents: .touchUpInside) self.infoButtonNode.addTarget(self, action: #selector(self.infoPressed), forControlEvents: .touchUpInside)
self.locationButtonNode.addTarget(self, action: #selector(self.locationPressed), forControlEvents: .touchUpInside) self.locationButtonNode.addTarget(self, action: #selector(self.locationPressed), forControlEvents: .touchUpInside)
self.placesButtonNode.addTarget(self, action: #selector(self.placesPressed), forControlEvents: .touchUpInside)
} }
func updateState(mapMode: LocationMapMode, displayingMapModeOptions: Bool) { func updateState(mapMode: LocationMapMode, displayingMapModeOptions: Bool, displayingPlacesButton: Bool, animated: Bool) {
self.mapNode.mapMode = mapMode self.mapNode.mapMode = mapMode
self.infoButtonNode.isSelected = displayingMapModeOptions self.infoButtonNode.isSelected = displayingMapModeOptions
let updateLayout = self.displayingPlacesButton != displayingPlacesButton
self.displayingPlacesButton = displayingPlacesButton
if updateLayout, let (layout, navigationBarHeight, topPadding, offset, size) = self.validLayout {
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .spring) : .immediate
self.updateLayout(layout: layout, navigationBarHeight: navigationBarHeight, topPadding: topPadding, offset: offset, size: size, transition: transition)
}
} }
func updatePresentationData(_ presentationData: PresentationData) { func updatePresentationData(_ presentationData: PresentationData) {
self.presentationData = presentationData self.presentationData = presentationData
self.optionsBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme) self.optionsBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
self.optionsSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal) self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected) self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted]) self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal) self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
self.placesBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
self.shadowNode.image = generateShadowImage(theme: presentationData.theme, highlighted: false) self.shadowNode.image = generateShadowImage(theme: presentationData.theme, highlighted: false)
} }
func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, topPadding: CGFloat, offset: CGFloat, size: CGSize, transition: ContainedViewLayoutTransition) { func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, topPadding: CGFloat, offset: CGFloat, size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (layout, navigationBarHeight, topPadding, offset, size)
let mapHeight: CGFloat = floor(layout.size.height * 1.5) let mapHeight: CGFloat = floor(layout.size.height * 1.5)
let mapFrame = CGRect(x: 0.0, y: floorToScreenPixels((size.height - mapHeight + navigationBarHeight) / 2.0) + offset, width: size.width, height: mapHeight) let mapFrame = CGRect(x: 0.0, y: floorToScreenPixels((size.height - mapHeight + navigationBarHeight) / 2.0) + offset, width: size.width, height: mapHeight)
transition.updateFrame(node: self.mapNode, frame: mapFrame) transition.updateFrame(node: self.mapNode, frame: mapFrame)
self.mapNode.updateLayout(size: mapFrame.size) self.mapNode.updateLayout(size: mapFrame.size)
let inset: CGFloat = 6.0
let placesButtonSize = CGSize(width: 180.0 + panelInset * 2.0, height: 45.0 + panelInset * 2.0)
let placesButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - placesButtonSize.width) / 2.0), y: self.displayingPlacesButton ? navigationBarHeight + topPadding + inset : 0.0), size: placesButtonSize)
transition.updateFrame(node: self.placesBackgroundNode, frame: placesButtonFrame)
transition.updateFrame(node: self.placesButtonNode, frame: CGRect(origin: CGPoint(), size: placesButtonSize))
transition.updateFrame(node: self.shadowNode, frame: CGRect(x: 0.0, y: size.height - 14.0, width: size.width, height: 14.0)) transition.updateFrame(node: self.shadowNode, frame: CGRect(x: 0.0, y: size.height - 14.0, width: size.width, height: 14.0))
let inset: CGFloat = 6.0
transition.updateFrame(node: self.optionsBackgroundNode, frame: CGRect(x: size.width - inset - panelSize.width - panelInset * 2.0, y: navigationBarHeight + topPadding + inset, width: panelSize.width + panelInset * 2.0, height: panelSize.height + panelInset * 2.0)) transition.updateFrame(node: self.optionsBackgroundNode, frame: CGRect(x: size.width - inset - panelSize.width - panelInset * 2.0, y: navigationBarHeight + topPadding + inset, width: panelSize.width + panelInset * 2.0, height: panelSize.height + panelInset * 2.0))
transition.updateFrame(node: self.infoButtonNode, frame: CGRect(x: panelInset, y: panelInset, width: panelSize.width, height: panelSize.height / 2.0)) transition.updateFrame(node: self.infoButtonNode, frame: CGRect(x: panelInset, y: panelInset, width: panelSize.width, height: panelSize.height / 2.0))
transition.updateFrame(node: self.locationButtonNode, frame: CGRect(x: panelInset, y: panelInset + panelSize.height / 2.0, width: panelSize.width, height: panelSize.height / 2.0)) transition.updateFrame(node: self.locationButtonNode, frame: CGRect(x: panelInset, y: panelInset + panelSize.height / 2.0, width: panelSize.width, height: panelSize.height / 2.0))
transition.updateFrame(node: self.optionsSeparatorNode, frame: CGRect(x: panelInset, y: panelInset + panelSize.height / 2.0, width: panelSize.width, height: UIScreenPixel))
let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
let optionsAlpha: CGFloat = size.height > 160.0 + navigationBarHeight ? 1.0 : 0.0 let optionsAlpha: CGFloat = size.height > 160.0 + navigationBarHeight ? 1.0 : 0.0
@ -131,4 +174,8 @@ final class LocationMapHeaderNode: ASDisplayNode {
@objc private func locationPressed() { @objc private func locationPressed() {
self.goToUserLocation() self.goToUserLocation()
} }
@objc private func placesPressed() {
self.showPlacesInThisArea()
}
} }

View File

@ -32,8 +32,9 @@ class LocationPickerInteraction {
let dismissInput: () -> Void let dismissInput: () -> Void
let updateSendActionHighlight: (Bool) -> Void let updateSendActionHighlight: (Bool) -> Void
let openHomeWorkInfo: () -> Void let openHomeWorkInfo: () -> Void
let showPlacesInThisArea: () -> Void
init(sendLocation: @escaping (CLLocationCoordinate2D) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D) -> Void, sendVenue: @escaping (TelegramMediaMap) -> Void, toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, goToUserLocation: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, openSearch: @escaping () -> Void, updateSearchQuery: @escaping (String) -> Void, dismissSearch: @escaping () -> Void, dismissInput: @escaping () -> Void, updateSendActionHighlight: @escaping (Bool) -> Void, openHomeWorkInfo: @escaping () -> Void) { init(sendLocation: @escaping (CLLocationCoordinate2D) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D) -> Void, sendVenue: @escaping (TelegramMediaMap) -> Void, toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, goToUserLocation: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, openSearch: @escaping () -> Void, updateSearchQuery: @escaping (String) -> Void, dismissSearch: @escaping () -> Void, dismissInput: @escaping () -> Void, updateSendActionHighlight: @escaping (Bool) -> Void, openHomeWorkInfo: @escaping () -> Void, showPlacesInThisArea: @escaping ()-> Void) {
self.sendLocation = sendLocation self.sendLocation = sendLocation
self.sendLiveLocation = sendLiveLocation self.sendLiveLocation = sendLiveLocation
self.sendVenue = sendVenue self.sendVenue = sendVenue
@ -47,6 +48,7 @@ class LocationPickerInteraction {
self.dismissInput = dismissInput self.dismissInput = dismissInput
self.updateSendActionHighlight = updateSendActionHighlight self.updateSendActionHighlight = updateSendActionHighlight
self.openHomeWorkInfo = openHomeWorkInfo self.openHomeWorkInfo = openHomeWorkInfo
self.showPlacesInThisArea = showPlacesInThisArea
} }
} }
@ -198,6 +200,7 @@ public final class LocationPickerController: ViewController {
var state = state var state = state
state.displayingMapModeOptions = false state.displayingMapModeOptions = false
state.selectedLocation = .none state.selectedLocation = .none
state.searchingVenuesAround = false
return state return state
} }
}, goToCoordinate: { [weak self] coordinate in }, goToCoordinate: { [weak self] coordinate in
@ -208,6 +211,7 @@ public final class LocationPickerController: ViewController {
var state = state var state = state
state.displayingMapModeOptions = false state.displayingMapModeOptions = false
state.selectedLocation = .location(coordinate, nil) state.selectedLocation = .location(coordinate, nil)
state.searchingVenuesAround = false
return state return state
} }
}, openSearch: { [weak self] in }, openSearch: { [weak self] in
@ -259,9 +263,13 @@ public final class LocationPickerController: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let controller = textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Map_HomeAndWorkTitle, text: strongSelf.presentationData.strings.Map_HomeAndWorkInfo, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]) let controller = textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Map_HomeAndWorkTitle, text: strongSelf.presentationData.strings.Map_HomeAndWorkInfo, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})])
strongSelf.present(controller, in: .window(.root)) strongSelf.present(controller, in: .window(.root))
}, showPlacesInThisArea: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.controllerNode.requestPlacesAtSelectedLocation()
}) })
self.scrollToTop = { [weak self] in self.scrollToTop = { [weak self] in

View File

@ -227,12 +227,14 @@ struct LocationPickerState {
var displayingMapModeOptions: Bool var displayingMapModeOptions: Bool
var selectedLocation: LocationPickerLocation var selectedLocation: LocationPickerLocation
var forceSelection: Bool var forceSelection: Bool
var searchingVenuesAround: Bool
init() { init() {
self.mapMode = .map self.mapMode = .map
self.displayingMapModeOptions = false self.displayingMapModeOptions = false
self.selectedLocation = .none self.selectedLocation = .none
self.forceSelection = false self.forceSelection = false
self.searchingVenuesAround = false
} }
} }
@ -259,6 +261,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
private let statePromise: Promise<LocationPickerState> private let statePromise: Promise<LocationPickerState>
private var geocodingDisposable = MetaDisposable() private var geocodingDisposable = MetaDisposable()
private let searchVenuesPromise = Promise<CLLocationCoordinate2D?>()
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
private var listOffset: CGFloat? private var listOffset: CGFloat?
@ -277,7 +281,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
self.listNode.verticalScrollIndicatorColor = UIColor(white: 0.0, alpha: 0.3) self.listNode.verticalScrollIndicatorColor = UIColor(white: 0.0, alpha: 0.3)
self.listNode.verticalScrollIndicatorFollowsOverscroll = true self.listNode.verticalScrollIndicatorFollowsOverscroll = true
self.headerNode = LocationMapHeaderNode(presentationData: presentationData, toggleMapModeSelection: interaction.toggleMapModeSelection, goToUserLocation: interaction.goToUserLocation) self.headerNode = LocationMapHeaderNode(presentationData: presentationData, toggleMapModeSelection: interaction.toggleMapModeSelection, goToUserLocation: interaction.goToUserLocation, showPlacesInThisArea: interaction.showPlacesInThisArea)
self.headerNode.mapNode.isRotateEnabled = false self.headerNode.mapNode.isRotateEnabled = false
self.optionsNode = LocationOptionsNode(presentationData: presentationData, updateMapMode: interaction.updateMapMode) self.optionsNode = LocationOptionsNode(presentationData: presentationData, updateMapMode: interaction.updateMapMode)
@ -408,13 +412,30 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
} }
) )
let foundVenues: Signal<[TelegramMediaMap]?, NoError> = .single(nil)
|> then(
self.searchVenuesPromise.get()
|> distinctUntilChanged
|> mapToSignal { coordinate -> Signal<[TelegramMediaMap]?, NoError> in
if let coordinate = coordinate {
return (.single(nil)
|> then(
nearbyVenues(account: context.account, latitude: coordinate.latitude, longitude: coordinate.longitude)
|> map (Optional.init)
))
} else {
return .single(nil)
}
}
)
let previousState = Atomic<LocationPickerState>(value: self.state) let previousState = Atomic<LocationPickerState>(value: self.state)
let previousUserLocation = Atomic<CLLocation?>(value: nil) let previousUserLocation = Atomic<CLLocation?>(value: nil)
let previousAnnotations = Atomic<[LocationPinAnnotation]>(value: []) let previousAnnotations = Atomic<[LocationPinAnnotation]>(value: [])
let previousEntries = Atomic<[LocationPickerEntry]?>(value: nil) let previousEntries = Atomic<[LocationPickerEntry]?>(value: nil)
self.disposable = (combineLatest(self.presentationDataPromise.get(), self.statePromise.get(), userLocation, venues) self.disposable = (combineLatest(self.presentationDataPromise.get(), self.statePromise.get(), userLocation, venues, foundVenues)
|> deliverOnMainQueue).start(next: { [weak self] presentationData, state, userLocation, venues in |> deliverOnMainQueue).start(next: { [weak self] presentationData, state, userLocation, venues, foundVenues in
if let strongSelf = self { if let strongSelf = self {
var entries: [LocationPickerEntry] = [] var entries: [LocationPickerEntry] = []
switch state.selectedLocation { switch state.selectedLocation {
@ -462,7 +483,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
entries.append(.header(presentationData.theme, presentationData.strings.Map_ChooseAPlace.uppercased())) entries.append(.header(presentationData.theme, presentationData.strings.Map_ChooseAPlace.uppercased()))
if let venues = venues { var displayedVenues = state.searchingVenuesAround ? foundVenues : venues
if let venues = displayedVenues {
var index: Int = 0 var index: Int = 0
for venue in venues { for venue in venues {
entries.append(.venue(presentationData.theme, venue, index)) entries.append(.venue(presentationData.theme, venue, index))
@ -480,11 +502,10 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
crossFade = true crossFade = true
} }
let transition = preparedTransition(from: previousEntries ?? [], to: entries, isLoading: venues == nil, crossFade: crossFade, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction) let transition = preparedTransition(from: previousEntries ?? [], to: entries, isLoading: displayedVenues == nil, crossFade: crossFade, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction)
strongSelf.enqueueTransition(transition) strongSelf.enqueueTransition(transition)
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions) var displayingPlacesButton = false
let previousUserLocation = previousUserLocation.swap(userLocation) let previousUserLocation = previousUserLocation.swap(userLocation)
switch state.selectedLocation { switch state.selectedLocation {
case .none: case .none:
@ -492,9 +513,11 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
strongSelf.headerNode.mapNode.setMapCenter(coordinate: userLocation.coordinate, isUserLocation: true, animated: previousUserLocation != nil) strongSelf.headerNode.mapNode.setMapCenter(coordinate: userLocation.coordinate, isUserLocation: true, animated: previousUserLocation != nil)
} }
strongSelf.headerNode.mapNode.resetAnnotationSelection() strongSelf.headerNode.mapNode.resetAnnotationSelection()
strongSelf.searchVenuesPromise.set(.single(nil))
case .selecting: case .selecting:
strongSelf.headerNode.mapNode.resetAnnotationSelection() strongSelf.headerNode.mapNode.resetAnnotationSelection()
case let .location(coordinate, _): strongSelf.searchVenuesPromise.set(.single(nil))
case let .location(coordinate, address):
var updateMap = false var updateMap = false
switch previousState.selectedLocation { switch previousState.selectedLocation {
case .none, .venue: case .none, .venue:
@ -510,12 +533,19 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
strongSelf.headerNode.mapNode.setMapCenter(coordinate: coordinate, isUserLocation: false, animated: true) strongSelf.headerNode.mapNode.setMapCenter(coordinate: coordinate, isUserLocation: false, animated: true)
strongSelf.headerNode.mapNode.switchToPicking(animated: false) strongSelf.headerNode.mapNode.switchToPicking(animated: false)
} }
if address != nil {
displayingPlacesButton = foundVenues == nil
}
case let .venue(venue): case let .venue(venue):
strongSelf.headerNode.mapNode.setMapCenter(coordinate: venue.coordinate, animated: true) strongSelf.headerNode.mapNode.setMapCenter(coordinate: venue.coordinate, animated: true)
strongSelf.searchVenuesPromise.set(.single(nil))
} }
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: displayingPlacesButton, animated: true)
let annotations: [LocationPinAnnotation] let annotations: [LocationPinAnnotation]
if let venues = venues { if let venues = displayedVenues {
annotations = venues.compactMap { LocationPinAnnotation(context: context, theme: presentationData.theme, location: $0) } annotations = venues.compactMap { LocationPinAnnotation(context: context, theme: presentationData.theme, location: $0) }
} else { } else {
annotations = [] annotations = []
@ -533,6 +563,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
updateLayout = true updateLayout = true
} else if previousState.selectedLocation.isCustom != state.selectedLocation.isCustom { } else if previousState.selectedLocation.isCustom != state.selectedLocation.isCustom {
updateLayout = true updateLayout = true
} else if previousState.searchingVenuesAround != state.searchingVenuesAround {
updateLayout = true
} }
if updateLayout { if updateLayout {
@ -597,6 +629,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
var state = state var state = state
state.displayingMapModeOptions = false state.displayingMapModeOptions = false
state.selectedLocation = .selecting state.selectedLocation = .selecting
state.searchingVenuesAround = false
return state return state
} }
} }
@ -609,6 +642,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
var state = state var state = state
if case .selecting = state.selectedLocation { if case .selecting = state.selectedLocation {
state.selectedLocation = .location(coordinate, nil) state.selectedLocation = .location(coordinate, nil)
state.searchingVenuesAround = false
} }
return state return state
} }
@ -622,6 +656,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
var state = state var state = state
state.displayingMapModeOptions = false state.displayingMapModeOptions = false
state.selectedLocation = annotation?.location.flatMap { .venue($0) } ?? .none state.selectedLocation = annotation?.location.flatMap { .venue($0) } ?? .none
state.searchingVenuesAround = false
return state return state
} }
} }
@ -634,6 +669,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
var state = state var state = state
state.displayingMapModeOptions = false state.displayingMapModeOptions = false
state.selectedLocation = .none state.selectedLocation = .none
state.searchingVenuesAround = false
return state return state
} }
} }
@ -746,7 +782,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
let isFirstLayout = self.validLayout == nil let isFirstLayout = self.validLayout == nil
self.validLayout = (layout, navigationHeight) self.validLayout = (layout, navigationHeight)
let isPickingLocation = self.state.selectedLocation.isCustom || self.state.forceSelection let isPickingLocation = (self.state.selectedLocation.isCustom || self.state.forceSelection) && !self.state.searchingVenuesAround
let optionsHeight: CGFloat = 38.0 let optionsHeight: CGFloat = 38.0
var actionHeight: CGFloat? var actionHeight: CGFloat?
self.listNode.forEachItemNode { itemNode in self.listNode.forEachItemNode { itemNode in
@ -819,4 +855,15 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
self.headerNode.updateHighlight(highlighted) self.headerNode.updateHighlight(highlighted)
self.shadeNode.backgroundColor = highlighted ? self.presentationData.theme.list.itemHighlightedBackgroundColor : self.presentationData.theme.list.plainBackgroundColor self.shadeNode.backgroundColor = highlighted ? self.presentationData.theme.list.itemHighlightedBackgroundColor : self.presentationData.theme.list.plainBackgroundColor
} }
func requestPlacesAtSelectedLocation() {
if case let .location(coordinate, _) = self.state.selectedLocation {
self.searchVenuesPromise.set(.single(coordinate))
self.updateState { state in
var state = state
state.searchingVenuesAround = true
return state
}
}
}
} }

View File

@ -258,7 +258,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode {
let transition = preparedTransition(from: previousEntries ?? [], to: entries, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction) let transition = preparedTransition(from: previousEntries ?? [], to: entries, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction)
strongSelf.enqueueTransition(transition) strongSelf.enqueueTransition(transition)
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions) strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, animated: false)
switch state.selectedLocation { switch state.selectedLocation {
case .initial: case .initial: