Various improvements

This commit is contained in:
Ilya Laktyushin
2025-10-16 05:30:06 +04:00
parent babf5cc9d7
commit 0915a42e64
289 changed files with 9723 additions and 2071 deletions

View File

@@ -16,6 +16,13 @@ import CoreLocation
import Geocoding
import PhoneNumberFormat
import DeviceAccess
import GlassBarButtonComponent
import ComponentFlow
import BundleIconComponent
import MultilineTextComponent
import SearchInputPanelComponent
import ButtonComponent
import EdgeEffect
private struct LocationPickerTransaction {
let deletions: [ListViewDeleteItem]
@@ -175,9 +182,9 @@ private enum LocationPickerEntry: Comparable, Identifiable {
icon = .location
}
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), engine: engine, title: title, subtitle: subtitle, icon: icon, beginTimeAndTimeout: nil, action: {
if let venue = venue {
if let venue {
interaction?.sendVenue(venue, queryId, resultId)
} else if let coordinate = coordinate {
} else if let coordinate {
interaction?.sendLocation(coordinate, name, address)
}
}, highlighted: { highlighted in
@@ -192,10 +199,10 @@ private enum LocationPickerEntry: Comparable, Identifiable {
}
})
case let .header(_, title):
return LocationSectionHeaderItem(presentationData: ItemListPresentationData(presentationData), title: title)
return LocationSectionHeaderItem(presentationData: ItemListPresentationData(presentationData), systemStyle: .legacy, title: title)
case let .venue(_, venue, queryId, resultId, _):
let venueType = venue?.venue?.type ?? ""
return ItemListVenueItem(presentationData: ItemListPresentationData(presentationData), engine: engine, venue: venue, style: .plain, action: venue.flatMap { venue in
return ItemListVenueItem(presentationData: ItemListPresentationData(presentationData), systemStyle: .glass, engine: engine, venue: venue, style: .plain, action: venue.flatMap { venue in
return { interaction?.sendVenue(venue, queryId, resultId) }
}, infoAction: ["home", "work"].contains(venueType) ? {
interaction?.openHomeWorkInfo()
@@ -344,6 +351,14 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
private let shadeNode: ASDisplayNode
private let innerShadeNode: ASDisplayNode
private let cancelButton = ComponentView<Empty>()
private let searchButton = ComponentView<Empty>()
private let title = ComponentView<Empty>()
fileprivate let bottomEdgeEffectView: EdgeEffectView
private var sendButton: ComponentView<Empty>?
private let optionsNode: LocationOptionsNode
private(set) var searchContainerNode: LocationSearchContainerNode?
@@ -360,6 +375,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
private let searchVenuesPromise = Promise<CLLocationCoordinate2D?>()
private var searchInput: ComponentView<Empty>?
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
private var listOffset: CGFloat?
@@ -394,9 +411,16 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
self.emptyResultsTextNode.textAlignment = .center
self.emptyResultsTextNode.isHidden = true
self.headerNode = LocationMapHeaderNode(presentationData: presentationData, toggleMapModeSelection: interaction.toggleMapModeSelection, goToUserLocation: interaction.goToUserLocation, showPlacesInThisArea: interaction.showPlacesInThisArea)
self.headerNode = LocationMapHeaderNode(
presentationData: presentationData,
glass: true,
toggleMapModeSelection: interaction.toggleMapModeSelection,
updateMapMode: interaction.updateMapMode,
goToUserLocation: interaction.goToUserLocation,
showPlacesInThisArea: interaction.showPlacesInThisArea
)
self.headerNode.mapNode.isRotateEnabled = false
self.optionsNode = LocationOptionsNode(presentationData: presentationData, updateMapMode: interaction.updateMapMode)
self.shadeNode = ASDisplayNode()
@@ -405,13 +429,16 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
self.innerShadeNode = ASDisplayNode()
self.innerShadeNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.bottomEdgeEffectView = EdgeEffectView()
self.bottomEdgeEffectView.isUserInteractionEnabled = false
super.init()
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.addSubnode(self.listNode)
self.addSubnode(self.headerNode)
self.addSubnode(self.optionsNode)
//self.addSubnode(self.optionsNode)
self.listNode.addSubnode(self.emptyResultsTextNode)
self.shadeNode.addSubnode(self.innerShadeNode)
self.addSubnode(self.shadeNode)
@@ -780,36 +807,34 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
if annotations != previousAnnotations {
strongSelf.headerNode.mapNode.annotations = annotations
}
var updateLayout = false
if [.denied, .restricted].contains(access) {
if !strongSelf.locationAccessDenied {
strongSelf.locationAccessDenied = true
strongSelf.locationAccessDeniedUpdated(true)
updateLayout = true
}
} else {
if strongSelf.locationAccessDenied {
strongSelf.locationAccessDenied = false
strongSelf.locationAccessDeniedUpdated(false)
updateLayout = true
}
}
if let (layout, navigationBarHeight) = strongSelf.validLayout {
var updateLayout = false
let transition: ContainedViewLayoutTransition = .animated(duration: 0.45, curve: .spring)
if case let .location(_, previousAddress, _) = previousState.selectedLocation, case let .location(_, newAddress, _) = state.selectedLocation, previousAddress != newAddress {
updateLayout = true
} else if previousState.displayingMapModeOptions != state.displayingMapModeOptions {
updateLayout = true
} else if previousState.selectedLocation.isCustom != state.selectedLocation.isCustom {
updateLayout = true
} else if previousState.searchingVenuesAround != state.searchingVenuesAround {
updateLayout = true
}
if [.denied, .restricted].contains(access) {
if !strongSelf.locationAccessDenied {
strongSelf.locationAccessDenied = true
strongSelf.locationAccessDeniedUpdated(true)
updateLayout = true
}
} else {
if strongSelf.locationAccessDenied {
strongSelf.locationAccessDenied = false
strongSelf.locationAccessDeniedUpdated(false)
updateLayout = true
}
}
if previousState.displayingMapModeOptions != state.displayingMapModeOptions {
updateLayout = true
} else if previousState.selectedLocation.isCustom != state.selectedLocation.isCustom {
updateLayout = true
} else if previousState.searchingVenuesAround != state.searchingVenuesAround {
updateLayout = true
}
if updateLayout {
strongSelf.containerLayoutUpdated(layout, navigationHeight: navigationBarHeight, transition: transition)
}
if updateLayout {
strongSelf.requestLayout(transition: .animated(duration: 0.45, curve: .spring))
}
let locale = localeWithStrings(presentationData.strings)
@@ -907,7 +932,13 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
setupGeocoding(coordinate, false, { [weak self] geoAddress, _, address, cityName, streetName, countryCode, isStreet in
self?.updateState { state in
var state = state
state.selectedLocation = .location(coordinate, address, global)
var shortAddress = ""
if let streetName {
shortAddress.append(streetName)
}
state.selectedLocation = .location(coordinate, shortAddress, global)
state.geoAddress = geoAddress
state.city = cityName
state.street = streetName
@@ -950,15 +981,15 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
}
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, listTransition in
guard let strongSelf = self, let (layout, navigationBarHeight) = strongSelf.validLayout, strongSelf.listNode.scrollEnabled else {
guard let self, let (layout, navigationBarHeight) = self.validLayout, self.listNode.scrollEnabled else {
return
}
let overlap: CGFloat = 6.0
strongSelf.listOffset = max(0.0, offset)
let overlap: CGFloat = 0.0 //6.0
self.listOffset = max(0.0, offset)
let headerFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: max(0.0, offset + overlap)))
listTransition.updateFrame(node: strongSelf.headerNode, frame: headerFrame)
strongSelf.headerNode.updateLayout(layout: layout, navigationBarHeight: navigationBarHeight, topPadding: strongSelf.state.displayingMapModeOptions ? 38.0 : 0.0, controlsTopPadding: strongSelf.state.displayingMapModeOptions ? 38.0 : 0.0, offset: 0.0, size: headerFrame.size, transition: listTransition)
strongSelf.layoutEmptyResultsPlaceholder(transition: listTransition)
listTransition.updateFrame(node: self.headerNode, frame: headerFrame)
self.headerNode.updateLayout(layout: layout, navigationBarHeight: navigationBarHeight, topPadding: self.state.displayingMapModeOptions ? 38.0 : 0.0, controlsTopPadding: self.state.displayingMapModeOptions ? 38.0 : 0.0, controlsBottomPadding: self.isPickingLocation ? 94.0 : 0.0, offset: 0.0, size: headerFrame.size, transition: listTransition)
self.layoutEmptyResultsPlaceholder(transition: listTransition)
}
self.listNode.beganInteractiveDragging = { [weak self] _ in
@@ -973,17 +1004,19 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
}
self.headerNode.mapNode.beganInteractiveDragging = { [weak self] in
guard let strongSelf = self else {
guard let self, let controller = self.controller else {
return
}
strongSelf.beganInteractiveDragging()
strongSelf.updateState { state in
self.beganInteractiveDragging()
self.updateState { state in
var state = state
state.displayingMapModeOptions = false
state.selectedLocation = .selecting
state.searchingVenuesAround = false
return state
}
controller.updateTabBarVisibility(false, .animated(duration: 0.4, curve: .spring))
}
self.headerNode.mapNode.endedInteractiveDragging = { [weak self] coordinate in
@@ -1081,7 +1114,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
if let strongSelf = self {
strongSelf.emptyResultsTextNode.isHidden = transition.isLoading || !transition.isEmpty
strongSelf.emptyResultsTextNode.attributedText = NSAttributedString(string: strongSelf.presentationData.strings.Map_NoPlacesNearby, font: Font.regular(15.0), textColor: strongSelf.presentationData.theme.list.freeTextColor)
strongSelf.emptyResultsTextNode.attributedText = NSAttributedString(string: strongSelf.presentationData.strings.Map_NoPlacesNearby, font: Font.regular(15.0), textColor: strongSelf.presentationData.theme.list.itemSecondaryTextColor)
strongSelf.layoutEmptyResultsPlaceholder(transition: .immediate)
}
@@ -1089,17 +1122,17 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
}
func activateSearch(navigationBar: NavigationBar) -> Signal<Bool, NoError> {
guard let (layout, navigationBarHeight) = self.validLayout, self.searchContainerNode == nil, let coordinate = self.headerNode.mapNode.mapCenterCoordinate else {
guard self.searchContainerNode == nil, let coordinate = self.headerNode.mapNode.mapCenterCoordinate else {
return .complete()
}
let searchContainerNode = LocationSearchContainerNode(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, coordinate: coordinate, interaction: self.interaction, story: self.source == .story)
self.insertSubnode(searchContainerNode, belowSubnode: navigationBar)
self.searchContainerNode = searchContainerNode
searchContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.containerLayoutUpdated(layout, navigationHeight: navigationBarHeight, transition: .immediate)
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
return searchContainerNode.isSearching
}
@@ -1112,6 +1145,16 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
searchContainerNode?.removeFromSupernode()
})
self.searchContainerNode = nil
self.deactivateInput()
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
}
func deactivateInput() {
if let searchInputView = self.searchInput?.view as? SearchInputPanelComponent.View {
let _ = searchInputView.deactivateInput()
}
}
func scrollToTop() {
@@ -1140,12 +1183,27 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
let emptyTextSize = self.emptyResultsTextNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0, height: CGFloat.greatestFiniteMagnitude))
transition.updateFrame(node: self.emptyResultsTextNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - emptyTextSize.width) / 2.0), y: headerHeight + actionsInset + floor((layout.size.height - headerHeight - actionsInset - emptyTextSize.height - layout.intrinsicInsets.bottom - layout.additionalInsets.bottom) / 2.0)), size: emptyTextSize))
}
private var isPickingLocation: Bool {
return (self.state.selectedLocation.isCustom || self.state.forceSelection) && !self.state.searchingVenuesAround
}
func requestLayout(transition: ContainedViewLayoutTransition) {
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: transition)
}
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
let isFirstLayout = self.validLayout == nil
self.validLayout = (layout, navigationHeight)
let isPickingLocation = (self.state.selectedLocation.isCustom || self.state.forceSelection) && !self.state.searchingVenuesAround
var glass = false
if let controller = self.controller, controller._hasGlassStyle {
glass = true
}
let isPickingLocation = self.isPickingLocation
let optionsHeight: CGFloat = 38.0
var actionHeight: CGFloat?
self.listNode.forEachItemNode { itemNode in
@@ -1155,14 +1213,17 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
}
}
}
// let topInset: CGFloat = floor((layout.size.height - navigationHeight) / 2.0 + navigationHeight)
let topInset: CGFloat = 240.0
let overlap: CGFloat = 6.0
let topInset: CGFloat = glass ? 300.0 : 240.0
let overlap: CGFloat = glass ? 0.0 : 6.0
let headerHeight: CGFloat
if isPickingLocation, let actionHeight = actionHeight {
self.listOffset = topInset
headerHeight = layout.size.height - actionHeight - layout.intrinsicInsets.bottom + overlap - 2.0
if glass {
headerHeight = layout.size.height
} else {
headerHeight = layout.size.height - actionHeight - layout.intrinsicInsets.bottom + overlap - 2.0
}
} else if let listOffset = self.listOffset {
headerHeight = max(0.0, listOffset + overlap)
} else {
@@ -1171,7 +1232,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
let headerFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: headerHeight))
transition.updateFrame(node: self.headerNode, frame: headerFrame)
self.headerNode.updateLayout(layout: layout, navigationBarHeight: navigationHeight, topPadding: self.state.displayingMapModeOptions ? optionsHeight : 0.0, controlsTopPadding: self.state.displayingMapModeOptions ? optionsHeight : 0.0, offset: 0.0, size: headerFrame.size, transition: transition)
self.headerNode.updateLayout(layout: layout, navigationBarHeight: navigationHeight, topPadding: self.state.displayingMapModeOptions && !glass ? optionsHeight : 0.0, controlsTopPadding: self.state.displayingMapModeOptions && !glass ? optionsHeight : 0.0, controlsBottomPadding: isPickingLocation ? 94.0 : 0.0, offset: 0.0, size: headerFrame.size, transition: transition)
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
let scrollToItem: ListViewScrollToItem?
@@ -1267,6 +1328,248 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
self.controller?.updateTabBarAlpha(1.0, .immediate)
}
if glass {
let titleSize = self.title.update(
transition: ComponentTransition(transition),
component: AnyComponent(
MultilineTextComponent(
text: .plain(
NSAttributedString(
string: self.presentationData.strings.Map_ChooseLocationTitle,
font: Font.semibold(17.0),
textColor: self.headerNode.mapNode.mapMode == .map ? self.presentationData.theme.rootController.navigationBar.primaryTextColor : .white
)
)
)
),
environment: {},
containerSize: CGSize(width: 200.0, height: 40.0)
)
let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - titleSize.width) / 2.0), y: floorToScreenPixels((navigationHeight - titleSize.height) / 2.0) + 3.0), size: titleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
self.view.addSubview(titleView)
}
transition.updateFrame(view: titleView, frame: titleFrame)
}
let barButtonSize = CGSize(width: 40.0, height: 40.0)
let cancelButtonSize = self.cancelButton.update(
transition: ComponentTransition(transition),
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
isDark: self.presentationData.theme.overallDarkAppearance,
state: .glass,
component: AnyComponentWithIdentity(id: isPickingLocation ? "back" : "close", component: AnyComponent(
BundleIconComponent(
name: isPickingLocation ? "Navigation/Back" : "Navigation/Close",
tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor
)
)),
action: { [weak self] _ in
guard let self else {
return
}
if isPickingLocation {
self.goToUserLocation()
} else {
self.controller?.dismiss()
}
}
)),
environment: {},
containerSize: barButtonSize
)
let cancelButtonFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left + 16.0, y: 16.0), size: cancelButtonSize)
if let cancelButtonView = self.cancelButton.view {
if cancelButtonView.superview == nil {
self.view.addSubview(cancelButtonView)
}
transition.updateFrame(view: cancelButtonView, frame: cancelButtonFrame)
}
let searchButtonSize = self.searchButton.update(
transition: ComponentTransition(transition),
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
isDark: self.presentationData.theme.overallDarkAppearance,
state: .glass,
component: AnyComponentWithIdentity(id: "search", component: AnyComponent(
BundleIconComponent(
name: "Navigation/Search",
tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor
)
)),
action: { [weak self] _ in
self?.controller?.searchPressed()
}
)),
environment: {},
containerSize: barButtonSize
)
let searchButtonFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - 16.0 - searchButtonSize.width, y: 16.0), size: searchButtonSize)
if let searchButtonView = self.searchButton.view {
if searchButtonView.superview == nil {
self.view.addSubview(searchButtonView)
}
transition.updateFrameAsPositionAndBounds(layer: searchButtonView.layer, frame: searchButtonFrame)
if let _ = self.searchContainerNode {
transition.updateAlpha(layer: searchButtonView.layer, alpha: 0.0)
transition.updateTransformScale(layer: searchButtonView.layer, scale: 0.01)
} else {
transition.updateAlpha(layer: searchButtonView.layer, alpha: 1.0)
transition.updateTransformScale(layer: searchButtonView.layer, scale: 1.0)
}
}
if let _ = self.searchContainerNode {
let searchInput: ComponentView<Empty>
if let current = self.searchInput {
searchInput = current
} else {
searchInput = ComponentView()
self.searchInput = searchInput
}
let searchInputTransition: ComponentTransition = self.searchInput?.view == nil ? .immediate : ComponentTransition(transition)
let searchInputSize = searchInput.update(
transition: searchInputTransition,
component: AnyComponent(
SearchInputPanelComponent(
theme: self.presentationData.theme,
strings: self.presentationData.strings,
placeholder: self.presentationData.strings.Map_Search,
resetText: nil,
updated: { [weak self] query in
guard let self, let controller = self.controller else {
return
}
controller.updateSearchQuery(query)
},
cancel: { [weak self] in
guard let self, let controller = self.controller else {
return
}
controller.dismissSearchPressed()
}
)
),
environment: {},
containerSize: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: layout.size.height)
)
let bottomInset: CGFloat = layout.insets(options: .input).bottom
let searchInputFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.size.height - bottomInset - searchInputSize.height), size: searchInputSize)
if let searchInputView = searchInput.view as? SearchInputPanelComponent.View {
if searchInputView.superview == nil {
self.view.addSubview(searchInputView)
searchInputView.frame = CGRect(origin: CGPoint(x: searchInputFrame.minX, y: layout.size.height), size: searchInputFrame.size)
searchInputView.activateInput()
}
transition.updateFrame(view: searchInputView, frame: searchInputFrame)
}
} else if let searchInput = self.searchInput {
self.searchInput = nil
if let searchInputView = searchInput.view {
transition.updateFrame(view: searchInputView, frame: CGRect(origin: CGPoint(x: searchInputView.frame.minX, y: layout.size.height), size: searchInputView.frame.size), completion: { _ in
searchInputView.removeFromSuperview()
})
}
}
let bottomEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - 88.0 - layout.additionalInsets.bottom), size: CGSize(width: layout.size.width, height: 88.0))
transition.updateFrame(view: self.bottomEdgeEffectView, frame: bottomEdgeEffectFrame)
self.bottomEdgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, blur: true, alpha: 0.65, rect: bottomEdgeEffectFrame, edge: .bottom, edgeSize: bottomEdgeEffectFrame.height, transition: ComponentTransition(transition))
if self.bottomEdgeEffectView.superview == nil {
self.view.addSubview(self.bottomEdgeEffectView)
}
if isPickingLocation {
let sendButton: ComponentView<Empty>
if let current = self.sendButton {
sendButton = current
} else {
sendButton = ComponentView()
self.sendButton = sendButton
}
let buttonBackground = ButtonComponent.Background(
style: .glass,
color: self.presentationData.theme.list.itemCheckColors.fillColor,
foreground: self.presentationData.theme.list.itemCheckColors.foregroundColor,
pressedColor: self.presentationData.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
)
//TODO:localize
var buttonContents: [AnyComponentWithIdentity<Empty>] = [
AnyComponentWithIdentity(
id: AnyHashable("label"),
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "Send Location", font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
)
]
var address: String?
if case let .location(_, addressValue, _) = self.state.selectedLocation {
address = addressValue
}
buttonContents.append(
AnyComponentWithIdentity(
id: AnyHashable(address ?? "locating"),
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: address ?? self.presentationData.strings.Map_Locating, font: Font.medium(13.0), textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), paragraphAlignment: .center))))
)
)
let sendButtonSize = sendButton.update(
transition: ComponentTransition(transition),
component: AnyComponent(
ButtonComponent(
background: buttonBackground,
content: AnyComponentWithIdentity(
id: AnyHashable("send"),
component: AnyComponent(
VStack(buttonContents, spacing: 1.0)
)
),
action: { [weak self] in
guard let self else {
return
}
if case let .location(coordinate, _, _) = self.state.selectedLocation {
self.interaction.sendLocation(coordinate, nil, nil)
}
}
)
),
environment: {},
containerSize: CGSize(width: layout.size.width - 36.0 * 2.0, height: 52.0)
)
let sendButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - sendButtonSize.width) / 2.0), y: layout.size.height - insets.bottom - sendButtonSize.height - 14.0), size: sendButtonSize)
if let sendButtonView = sendButton.view {
if sendButtonView.superview == nil {
self.view.addSubview(sendButtonView)
sendButtonView.alpha = 0.0
sendButtonView.transform = .identity
transition.animateTransformScale(view: sendButtonView, from: 0.01)
transition.updateAlpha(layer: sendButtonView.layer, alpha: 1.0)
}
transition.updateFrameAsPositionAndBounds(layer: sendButtonView.layer, frame: sendButtonFrame)
}
} else if let sendButton = self.sendButton {
self.sendButton = nil
if let sendButtonView = sendButton.view {
transition.updateAlpha(layer: sendButtonView.layer, alpha: 0.0, completion: { _ in
sendButtonView.removeFromSuperview()
})
transition.updateTransformScale(layer: sendButtonView.layer, scale: 0.01)
}
}
}
}
func updateSendActionHighlight(_ highlighted: Bool) {
@@ -1275,6 +1578,10 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
}
func goToUserLocation() {
guard let controller = self.controller else {
return
}
self.searchVenuesPromise.set(.single(nil))
self.updateState { state in
var state = state
@@ -1283,6 +1590,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
state.searchingVenuesAround = false
return state
}
controller.updateTabBarVisibility(true, .animated(duration: 0.4, curve: .spring))
}
func requestPlacesAtSelectedLocation() {