mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Location Picker fixes
This commit is contained in:
parent
f1664fa0ba
commit
fedd9aca8d
@ -5153,3 +5153,4 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"Map.Work" = "Work";
|
"Map.Work" = "Work";
|
||||||
"Map.HomeAndWorkTitle" = "Home & Work Addresses";
|
"Map.HomeAndWorkTitle" = "Home & Work Addresses";
|
||||||
"Map.HomeAndWorkInfo" = "Telegram uses the Home and Work addresses from your Contact Card.\n\nKeep your Contact Card up to date for quick access to sending Home and Work addresses.";
|
"Map.HomeAndWorkInfo" = "Telegram uses the Home and Work addresses from your Contact Card.\n\nKeep your Contact Card up to date for quick access to sending Home and Work addresses.";
|
||||||
|
"Map.SearchNoResultsDescription" = "There were no results for \"%@\".\nTry a new search.";
|
||||||
|
|||||||
@ -16,5 +16,6 @@ static_library(
|
|||||||
],
|
],
|
||||||
frameworks = [
|
frameworks = [
|
||||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||||
|
"$SDKROOT/System/Library/Frameworks/CoreLocation.framework",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -244,7 +244,7 @@ public final class DeviceAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func authorizeAccess(to subject: DeviceAccessSubject, registerForNotifications: ((@escaping (Bool) -> Void) -> Void)? = nil, requestSiriAuthorization: ((@escaping (Bool) -> Void) -> Void)? = nil, locationManager: CLLocationManager? = nil, presentationData: PresentationData? = nil, present: @escaping (ViewController, Any?) -> Void = { _, _ in }, openSettings: @escaping () -> Void = { }, displayNotificationFromBackground: @escaping (String) -> Void = { _ in }, _ completion: @escaping (Bool) -> Void = { _ in }) {
|
public static func authorizeAccess(to subject: DeviceAccessSubject, registerForNotifications: ((@escaping (Bool) -> Void) -> Void)? = nil, requestSiriAuthorization: ((@escaping (Bool) -> Void) -> Void)? = nil, locationManager: LocationManager? = nil, presentationData: PresentationData? = nil, present: @escaping (ViewController, Any?) -> Void = { _, _ in }, openSettings: @escaping () -> Void = { }, displayNotificationFromBackground: @escaping (String) -> Void = { _ in }, _ completion: @escaping (Bool) -> Void = { _ in }) {
|
||||||
switch subject {
|
switch subject {
|
||||||
case .camera:
|
case .camera:
|
||||||
let status = PGCamera.cameraAuthorizationStatus()
|
let status = PGCamera.cameraAuthorizationStatus()
|
||||||
@ -383,10 +383,14 @@ public final class DeviceAccess {
|
|||||||
}
|
}
|
||||||
case .notDetermined:
|
case .notDetermined:
|
||||||
switch locationSubject {
|
switch locationSubject {
|
||||||
case .send:
|
case .send, .tracking:
|
||||||
locationManager?.requestWhenInUseAuthorization()
|
locationManager?.requestWhenInUseAuthorization(completion: { status in
|
||||||
|
completion(status == .authorizedWhenInUse || status == .authorizedAlways)
|
||||||
|
})
|
||||||
case .live:
|
case .live:
|
||||||
locationManager?.requestAlwaysAuthorization()
|
locationManager?.requestAlwaysAuthorization(completion: { status in
|
||||||
|
completion(status == .authorizedAlways)
|
||||||
|
})
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
38
submodules/DeviceAccess/Sources/LocationManager.swift
Normal file
38
submodules/DeviceAccess/Sources/LocationManager.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import Foundation
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
public final class LocationManager: NSObject, CLLocationManagerDelegate {
|
||||||
|
public let manager = CLLocationManager()
|
||||||
|
var pendingCompletion: ((CLAuthorizationStatus) -> Void, CLAuthorizationStatus)?
|
||||||
|
|
||||||
|
public override init() {
|
||||||
|
super.init()
|
||||||
|
self.manager.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestWhenInUseAuthorization(completion: @escaping (CLAuthorizationStatus) -> Void) {
|
||||||
|
let status = CLLocationManager.authorizationStatus()
|
||||||
|
if status == .notDetermined {
|
||||||
|
self.manager.requestWhenInUseAuthorization()
|
||||||
|
self.pendingCompletion = (completion, .authorizedWhenInUse)
|
||||||
|
} else {
|
||||||
|
completion(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestAlwaysAuthorization(completion: @escaping (CLAuthorizationStatus) -> Void) {
|
||||||
|
let status = CLLocationManager.authorizationStatus()
|
||||||
|
if status == .notDetermined {
|
||||||
|
self.manager.requestWhenInUseAuthorization()
|
||||||
|
self.pendingCompletion = (completion, .authorizedAlways)
|
||||||
|
} else {
|
||||||
|
completion(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
|
||||||
|
if let (pendingCompletion, _) = self.pendingCompletion {
|
||||||
|
pendingCompletion(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -230,10 +230,16 @@ class LocationPinAnnotationView: MKAnnotationView {
|
|||||||
|
|
||||||
|
|
||||||
if let annotation = self.annotation as? LocationPinAnnotation, let venue = annotation.location?.venue {
|
if let annotation = self.annotation as? LocationPinAnnotation, let venue = annotation.location?.venue {
|
||||||
|
var textColor = UIColor.black
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
if self.traitCollection.userInterfaceStyle == .dark {
|
||||||
|
textColor = .white
|
||||||
|
}
|
||||||
|
}
|
||||||
let labelNode = ImmediateTextNode()
|
let labelNode = ImmediateTextNode()
|
||||||
labelNode.displaysAsynchronously = false
|
labelNode.displaysAsynchronously = false
|
||||||
labelNode.isUserInteractionEnabled = false
|
labelNode.isUserInteractionEnabled = false
|
||||||
labelNode.attributedText = NSAttributedString(string: venue.title, font: Font.medium(10), textColor: .black)
|
labelNode.attributedText = NSAttributedString(string: venue.title, font: Font.medium(10), textColor: textColor)
|
||||||
labelNode.maximumNumberOfLines = 2
|
labelNode.maximumNumberOfLines = 2
|
||||||
labelNode.textAlignment = .center
|
labelNode.textAlignment = .center
|
||||||
labelNode.truncationType = .end
|
labelNode.truncationType = .end
|
||||||
@ -241,9 +247,10 @@ class LocationPinAnnotationView: MKAnnotationView {
|
|||||||
self.labelNode = labelNode
|
self.labelNode = labelNode
|
||||||
self.addSubnode(labelNode)
|
self.addSubnode(labelNode)
|
||||||
|
|
||||||
let size = labelNode.updateLayout(CGSize(width: 120.0, height: CGFloat.greatestFiniteMagnitude))
|
var size = labelNode.updateLayout(CGSize(width: 120.0, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
size.height += 2.0
|
||||||
labelNode.bounds = CGRect(origin: CGPoint(), size: size)
|
labelNode.bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
labelNode.position = CGPoint(x: 0.0, y: 10.0)
|
labelNode.position = CGPoint(x: 0.0, y: 10.0 + floor(size.height / 2.0))
|
||||||
|
|
||||||
labelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
labelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
} else {
|
} else {
|
||||||
@ -317,7 +324,7 @@ class LocationPinAnnotationView: MKAnnotationView {
|
|||||||
} else {
|
} else {
|
||||||
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 24.0))
|
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 24.0))
|
||||||
avatarNode.isLayerBacked = false
|
avatarNode.isLayerBacked = false
|
||||||
avatarNode.bounds = CGRect(origin: CGPoint(), size: CGSize(width: 55.0, height: 55.0))
|
avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 55.0, height: 55.0))
|
||||||
avatarNode.position = CGPoint()
|
avatarNode.position = CGPoint()
|
||||||
self.avatarNode = avatarNode
|
self.avatarNode = avatarNode
|
||||||
self.addSubnode(avatarNode)
|
self.addSubnode(avatarNode)
|
||||||
@ -326,6 +333,21 @@ class LocationPinAnnotationView: MKAnnotationView {
|
|||||||
avatarNode.setPeer(account: account, theme: theme, peer: peer)
|
avatarNode.setPeer(account: account, theme: theme, peer: peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
super.traitCollectionDidChange(previousTraitCollection)
|
||||||
|
|
||||||
|
if let labelNode = self.labelNode {
|
||||||
|
var textColor = UIColor.black
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
if self.traitCollection.userInterfaceStyle == .dark {
|
||||||
|
textColor = .white
|
||||||
|
}
|
||||||
|
}
|
||||||
|
labelNode.attributedText = NSAttributedString(string: labelNode.attributedText?.string ?? "", font: Font.medium(10), textColor: textColor)
|
||||||
|
let _ = labelNode.updateLayout(CGSize(width: 120.0, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var isRaised = false
|
var isRaised = false
|
||||||
func setRaised(_ raised: Bool, animated: Bool, completion: @escaping () -> Void = {}) {
|
func setRaised(_ raised: Bool, animated: Bool, completion: @escaping () -> Void = {}) {
|
||||||
guard raised != self.isRaised else {
|
guard raised != self.isRaised else {
|
||||||
|
|||||||
@ -64,7 +64,7 @@ public final class LocationPickerController: ViewController {
|
|||||||
private var searchNavigationContentNode: LocationSearchNavigationContentNode?
|
private var searchNavigationContentNode: LocationSearchNavigationContentNode?
|
||||||
private var isSearchingDisposable = MetaDisposable()
|
private var isSearchingDisposable = MetaDisposable()
|
||||||
|
|
||||||
private let locationManager = CLLocationManager()
|
private let locationManager = LocationManager()
|
||||||
private var permissionDisposable: Disposable?
|
private var permissionDisposable: Disposable?
|
||||||
|
|
||||||
private var interaction: LocationPickerInteraction?
|
private var interaction: LocationPickerInteraction?
|
||||||
@ -135,6 +135,14 @@ public final class LocationPickerController: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
DeviceAccess.authorizeAccess(to: .location(.live), locationManager: strongSelf.locationManager, presentationData: strongSelf.presentationData, present: { c, a in
|
||||||
|
strongSelf.present(c, in: .window(.root), with: a)
|
||||||
|
}, openSettings: {
|
||||||
|
strongSelf.context.sharedContext.applicationBindings.openSettings()
|
||||||
|
}) { [weak self] authorized in
|
||||||
|
guard let strongSelf = self, authorized else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let controller = ActionSheetController(presentationData: strongSelf.presentationData)
|
let controller = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||||
var title = strongSelf.presentationData.strings.Map_LiveLocationGroupDescription
|
var title = strongSelf.presentationData.strings.Map_LiveLocationGroupDescription
|
||||||
if case let .share(peer, _, _) = strongSelf.mode, let receiver = peer {
|
if case let .share(peer, _, _) = strongSelf.mode, let receiver = peer {
|
||||||
@ -172,6 +180,7 @@ public final class LocationPickerController: ViewController {
|
|||||||
])
|
])
|
||||||
])
|
])
|
||||||
strongSelf.present(controller, in: .window(.root))
|
strongSelf.present(controller, in: .window(.root))
|
||||||
|
}
|
||||||
}, sendVenue: { [weak self] venue in
|
}, sendVenue: { [weak self] venue in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -136,7 +136,9 @@ private enum LocationPickerEntry: Comparable, Identifiable {
|
|||||||
icon = .location
|
icon = .location
|
||||||
}
|
}
|
||||||
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: account, title: title, subtitle: subtitle, icon: icon, action: {
|
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: account, title: title, subtitle: subtitle, icon: icon, action: {
|
||||||
if let coordinate = coordinate {
|
if let venue = venue {
|
||||||
|
interaction?.sendVenue(venue)
|
||||||
|
} else if let coordinate = coordinate {
|
||||||
interaction?.sendLocation(coordinate)
|
interaction?.sendLocation(coordinate)
|
||||||
}
|
}
|
||||||
}, highlighted: { highlighted in
|
}, highlighted: { highlighted in
|
||||||
|
|||||||
@ -71,17 +71,19 @@ struct LocationSearchContainerTransition {
|
|||||||
let deletions: [ListViewDeleteItem]
|
let deletions: [ListViewDeleteItem]
|
||||||
let insertions: [ListViewInsertItem]
|
let insertions: [ListViewInsertItem]
|
||||||
let updates: [ListViewUpdateItem]
|
let updates: [ListViewUpdateItem]
|
||||||
|
let query: String
|
||||||
let isSearching: Bool
|
let isSearching: Bool
|
||||||
|
let isEmpty: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
private func locationSearchContainerPreparedTransition(from fromEntries: [LocationSearchEntry], to toEntries: [LocationSearchEntry], isSearching: Bool, account: Account, presentationData: PresentationData, sendVenue: @escaping (TelegramMediaMap) -> Void) -> LocationSearchContainerTransition {
|
private func locationSearchContainerPreparedTransition(from fromEntries: [LocationSearchEntry], to toEntries: [LocationSearchEntry], query: String, isSearching: Bool, isEmpty: Bool, account: Account, presentationData: PresentationData, sendVenue: @escaping (TelegramMediaMap) -> Void) -> LocationSearchContainerTransition {
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, sendVenue: sendVenue), directionHint: nil) }
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, sendVenue: sendVenue), directionHint: nil) }
|
||||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, sendVenue: sendVenue), directionHint: nil) }
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, sendVenue: sendVenue), directionHint: nil) }
|
||||||
|
|
||||||
return LocationSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching)
|
return LocationSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, query: query, isSearching: isSearching, isEmpty: isEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class LocationSearchContainerNode: ASDisplayNode {
|
final class LocationSearchContainerNode: ASDisplayNode {
|
||||||
@ -90,6 +92,8 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private let dimNode: ASDisplayNode
|
private let dimNode: ASDisplayNode
|
||||||
public let listNode: ListView
|
public let listNode: ListView
|
||||||
|
private let emptyResultsTitleNode: ImmediateTextNode
|
||||||
|
private let emptyResultsTextNode: ImmediateTextNode
|
||||||
|
|
||||||
private let searchQuery = Promise<String?>()
|
private let searchQuery = Promise<String?>()
|
||||||
private let searchDisposable = MetaDisposable()
|
private let searchDisposable = MetaDisposable()
|
||||||
@ -97,7 +101,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)>
|
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)>
|
||||||
|
|
||||||
private var containerViewLayout: (ContainerViewLayout, CGFloat)?
|
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||||
private var enqueuedTransitions: [LocationSearchContainerTransition] = []
|
private var enqueuedTransitions: [LocationSearchContainerTransition] = []
|
||||||
|
|
||||||
private let _isSearching = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let _isSearching = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
@ -109,10 +113,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.interaction = interaction
|
self.interaction = interaction
|
||||||
|
|
||||||
let currentLocation = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
|
|
||||||
|
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings))
|
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings))
|
||||||
|
|
||||||
self.dimNode = ASDisplayNode()
|
self.dimNode = ASDisplayNode()
|
||||||
@ -121,6 +122,16 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
self.listNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
self.listNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||||
self.listNode.isHidden = true
|
self.listNode.isHidden = true
|
||||||
|
|
||||||
|
self.emptyResultsTitleNode = ImmediateTextNode()
|
||||||
|
self.emptyResultsTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SharedMedia_SearchNoResults, font: Font.semibold(17.0), textColor: self.presentationData.theme.list.freeTextColor)
|
||||||
|
self.emptyResultsTitleNode.textAlignment = .center
|
||||||
|
self.emptyResultsTitleNode.isHidden = true
|
||||||
|
|
||||||
|
self.emptyResultsTextNode = ImmediateTextNode()
|
||||||
|
self.emptyResultsTextNode.maximumNumberOfLines = 0
|
||||||
|
self.emptyResultsTextNode.textAlignment = .center
|
||||||
|
self.emptyResultsTextNode.isHidden = true
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.backgroundColor = nil
|
self.backgroundColor = nil
|
||||||
@ -129,8 +140,12 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.dimNode)
|
self.addSubnode(self.dimNode)
|
||||||
self.addSubnode(self.listNode)
|
self.addSubnode(self.listNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.emptyResultsTitleNode)
|
||||||
|
self.addSubnode(self.emptyResultsTextNode)
|
||||||
|
|
||||||
self.listNode.isHidden = true
|
self.listNode.isHidden = true
|
||||||
|
|
||||||
|
let currentLocation = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
|
||||||
let themeAndStringsPromise = self.themeAndStringsPromise
|
let themeAndStringsPromise = self.themeAndStringsPromise
|
||||||
|
|
||||||
let isSearching = self._isSearching
|
let isSearching = self._isSearching
|
||||||
@ -143,7 +158,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
return .single(query)
|
return .single(query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> mapToSignal { query -> Signal<[LocationSearchEntry]?, NoError> in
|
|> mapToSignal { query -> Signal<([LocationSearchEntry], String)?, NoError> in
|
||||||
if let query = query, !query.isEmpty {
|
if let query = query, !query.isEmpty {
|
||||||
let foundVenues = nearbyVenues(account: context.account, latitude: coordinate.latitude, longitude: coordinate.longitude, query: query)
|
let foundVenues = nearbyVenues(account: context.account, latitude: coordinate.latitude, longitude: coordinate.longitude, query: query)
|
||||||
|> afterCompleted {
|
|> afterCompleted {
|
||||||
@ -155,7 +170,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
|> beforeStarted {
|
|> beforeStarted {
|
||||||
isSearching.set(true)
|
isSearching.set(true)
|
||||||
}
|
}
|
||||||
|> map { venues, placemarks, themeAndStrings -> [LocationSearchEntry] in
|
|> map { venues, placemarks, themeAndStrings -> ([LocationSearchEntry], String) in
|
||||||
var entries: [LocationSearchEntry] = []
|
var entries: [LocationSearchEntry] = []
|
||||||
var index: Int = 0
|
var index: Int = 0
|
||||||
|
|
||||||
@ -176,7 +191,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
entries.append(LocationSearchEntry(index: index, theme: themeAndStrings.0, location: venue, title: nil, distance: 0.0))
|
entries.append(LocationSearchEntry(index: index, theme: themeAndStrings.0, location: venue, title: nil, distance: 0.0))
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
return entries
|
return (entries, query)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -188,10 +203,11 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
let previousSearchItems = Atomic<[LocationSearchEntry]>(value: [])
|
let previousSearchItems = Atomic<[LocationSearchEntry]>(value: [])
|
||||||
self.searchDisposable.set((searchItems
|
self.searchDisposable.set((searchItems
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] items in
|
|> deliverOnMainQueue).start(next: { [weak self] itemsAndQuery in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
let (items, query) = itemsAndQuery ?? (nil, "")
|
||||||
let previousItems = previousSearchItems.swap(items ?? [])
|
let previousItems = previousSearchItems.swap(items ?? [])
|
||||||
let transition = locationSearchContainerPreparedTransition(from: previousItems, to: items ?? [], isSearching: items != nil, account: context.account, presentationData: strongSelf.presentationData, sendVenue: { venue in self?.listNode.clearHighlightAnimated(true)
|
let transition = locationSearchContainerPreparedTransition(from: previousItems, to: items ?? [], query: query, isSearching: items != nil, isEmpty: items?.isEmpty ?? false, account: context.account, presentationData: strongSelf.presentationData, sendVenue: { venue in self?.listNode.clearHighlightAnimated(true)
|
||||||
if let _ = venue.venue {
|
if let _ = venue.venue {
|
||||||
self?.interaction.sendVenue(venue)
|
self?.interaction.sendVenue(venue)
|
||||||
} else {
|
} else {
|
||||||
@ -239,8 +255,8 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
let hadValidLayout = self.containerViewLayout != nil
|
let hadValidLayout = self.validLayout != nil
|
||||||
self.containerViewLayout = (layout, navigationBarHeight)
|
self.validLayout = (layout, navigationBarHeight)
|
||||||
|
|
||||||
let topInset = navigationBarHeight
|
let topInset = navigationBarHeight
|
||||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset)))
|
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset)))
|
||||||
@ -248,6 +264,18 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
|
|
||||||
|
let padding: CGFloat = 16.0
|
||||||
|
let emptyTitleSize = self.emptyResultsTitleNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
let emptyTextSize = self.emptyResultsTextNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
|
||||||
|
let insets = layout.insets(options: [.input])
|
||||||
|
let emptyTextSpacing: CGFloat = 8.0
|
||||||
|
let emptyTotalHeight = emptyTitleSize.height + emptyTextSize.height + emptyTextSpacing
|
||||||
|
let emptyTitleY = navigationBarHeight + floorToScreenPixels((layout.size.height - navigationBarHeight - max(insets.bottom, layout.intrinsicInsets.bottom) - emptyTotalHeight) / 2.0)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.emptyResultsTitleNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + padding + (layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0 - emptyTitleSize.width) / 2.0, y: emptyTitleY), size: emptyTitleSize))
|
||||||
|
transition.updateFrame(node: self.emptyResultsTextNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + padding + (layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0 - emptyTextSize.width) / 2.0, y: emptyTitleY + emptyTitleSize.height + emptyTextSpacing), size: emptyTextSize))
|
||||||
|
|
||||||
if !hadValidLayout {
|
if !hadValidLayout {
|
||||||
while !self.enqueuedTransitions.isEmpty {
|
while !self.enqueuedTransitions.isEmpty {
|
||||||
self.dequeueTransition()
|
self.dequeueTransition()
|
||||||
@ -258,7 +286,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
private func enqueueTransition(_ transition: LocationSearchContainerTransition) {
|
private func enqueueTransition(_ transition: LocationSearchContainerTransition) {
|
||||||
self.enqueuedTransitions.append(transition)
|
self.enqueuedTransitions.append(transition)
|
||||||
|
|
||||||
if self.containerViewLayout != nil {
|
if self.validLayout != nil {
|
||||||
while !self.enqueuedTransitions.isEmpty {
|
while !self.enqueuedTransitions.isEmpty {
|
||||||
self.dequeueTransition()
|
self.dequeueTransition()
|
||||||
}
|
}
|
||||||
@ -273,10 +301,22 @@ final class LocationSearchContainerNode: ASDisplayNode {
|
|||||||
options.insert(.PreferSynchronousDrawing)
|
options.insert(.PreferSynchronousDrawing)
|
||||||
options.insert(.PreferSynchronousResourceLoading)
|
options.insert(.PreferSynchronousResourceLoading)
|
||||||
|
|
||||||
let isSearching = transition.isSearching
|
|
||||||
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
|
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||||
self?.listNode.isHidden = !isSearching
|
guard let strongSelf = self else {
|
||||||
self?.dimNode.isHidden = isSearching
|
return
|
||||||
|
}
|
||||||
|
strongSelf.listNode.isHidden = !transition.isSearching
|
||||||
|
strongSelf.dimNode.isHidden = transition.isSearching
|
||||||
|
|
||||||
|
strongSelf.emptyResultsTextNode.attributedText = NSAttributedString(string: strongSelf.presentationData.strings.Map_SearchNoResultsDescription(transition.query).0, font: Font.regular(15.0), textColor: strongSelf.presentationData.theme.list.freeTextColor)
|
||||||
|
|
||||||
|
let emptyResults = transition.isSearching && transition.isEmpty
|
||||||
|
strongSelf.emptyResultsTitleNode.isHidden = !emptyResults
|
||||||
|
strongSelf.emptyResultsTextNode.isHidden = !emptyResults
|
||||||
|
|
||||||
|
if let (layout, navigationBarHeight) = strongSelf.validLayout {
|
||||||
|
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -651,15 +651,33 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
|||||||
guard let venueData = venue.venue else {
|
guard let venueData = venue.venue else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
updateState { current in
|
updateState { current in
|
||||||
var current = current
|
var current = current
|
||||||
if current.editingName.isEmpty || current.nameSetFromVenue {
|
if current.editingName.isEmpty || current.nameSetFromVenue {
|
||||||
current.editingName = .title(title: venueData.title ?? "", type: .group)
|
current.editingName = .title(title: venueData.title ?? "", type: .group)
|
||||||
current.nameSetFromVenue = true
|
current.nameSetFromVenue = true
|
||||||
}
|
}
|
||||||
current.location = PeerGeoLocation(latitude: venue.latitude, longitude: venue.longitude, address: venueData.address ?? "")
|
current.location = PeerGeoLocation(latitude: venue.latitude, longitude: venue.longitude, address: presentationData.strings.Map_Locating + "\n\n")
|
||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = (reverseGeocodeLocation(latitude: venue.latitude, longitude: venue.longitude)
|
||||||
|
|> map { placemark -> String in
|
||||||
|
if let placemark = placemark {
|
||||||
|
return placemark.fullAddress
|
||||||
|
} else {
|
||||||
|
return venueData.address ?? ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue).start(next: { address in
|
||||||
|
addressPromise.set(.single(address))
|
||||||
|
updateState { current in
|
||||||
|
var current = current
|
||||||
|
current.location = PeerGeoLocation(latitude: venue.latitude, longitude: venue.longitude, address: address)
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
})
|
||||||
ensureItemVisibleImpl?(.info, true)
|
ensureItemVisibleImpl?(.info, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -149,7 +149,6 @@ class WebSearchControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private let attributionNode: ASImageNode
|
private let attributionNode: ASImageNode
|
||||||
|
|
||||||
private let recentQueriesPlaceholder: ImmediateTextNode
|
|
||||||
private let recentQueriesNode: ListView
|
private let recentQueriesNode: ListView
|
||||||
private var enqueuedRecentTransitions: [(WebSearchRecentTransition, Bool)] = []
|
private var enqueuedRecentTransitions: [(WebSearchRecentTransition, Bool)] = []
|
||||||
|
|
||||||
@ -216,8 +215,6 @@ class WebSearchControllerNode: ASDisplayNode {
|
|||||||
self.recentQueriesNode = ListView()
|
self.recentQueriesNode = ListView()
|
||||||
self.recentQueriesNode.backgroundColor = theme.list.plainBackgroundColor
|
self.recentQueriesNode.backgroundColor = theme.list.plainBackgroundColor
|
||||||
|
|
||||||
self.recentQueriesPlaceholder = ImmediateTextNode()
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.setViewBlock({
|
self.setViewBlock({
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user