mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '33d946f8db623b378af48d9d56c8d9d9406bbabf' into experimental-2
This commit is contained in:
commit
a33810566d
@ -4366,9 +4366,7 @@ Sorry for the inconvenience.";
|
|||||||
"Channel.DiscussionGroup.UnlinkGroup" = "Unlink Group";
|
"Channel.DiscussionGroup.UnlinkGroup" = "Unlink Group";
|
||||||
"Channel.DiscussionGroup.UnlinkChannel" = "Unlink Channel";
|
"Channel.DiscussionGroup.UnlinkChannel" = "Unlink Channel";
|
||||||
"Channel.DiscussionGroup.PublicChannelLink" = "Do you want to make %1$@ the discussion board for %2$@?";
|
"Channel.DiscussionGroup.PublicChannelLink" = "Do you want to make %1$@ the discussion board for %2$@?";
|
||||||
"Channel.DiscussionGroup.PrivateChannelLink" = "Do you want to make %1$@ the discussion board for %2$@?
|
"Channel.DiscussionGroup.PrivateChannelLink" = "Do you want to make %1$@ the discussion board for %2$@?\n\nAny member of this group will be able to see messages in the channel.";
|
||||||
|
|
||||||
Any member of this group will be able to see messages in the channel.";
|
|
||||||
"Channel.DiscussionGroup.MakeHistoryPublic" = "Warning: If you set this private group as the disccussion group for your channel, all channel subscribers will be able to access the group. \"Chat history for new members\" will be switched to Visible.";
|
"Channel.DiscussionGroup.MakeHistoryPublic" = "Warning: If you set this private group as the disccussion group for your channel, all channel subscribers will be able to access the group. \"Chat history for new members\" will be switched to Visible.";
|
||||||
"Channel.DiscussionGroup.MakeHistoryPublicProceed" = "Proceed";
|
"Channel.DiscussionGroup.MakeHistoryPublicProceed" = "Proceed";
|
||||||
|
|
||||||
@ -5840,6 +5838,7 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
|
|
||||||
"Notification.ProximityReached" = "%1$@ is now within %2$@ from %3$@";
|
"Notification.ProximityReached" = "%1$@ is now within %2$@ from %3$@";
|
||||||
"Notification.ProximityReachedYou" = "%1$@ is now within %2$@ from you";
|
"Notification.ProximityReachedYou" = "%1$@ is now within %2$@ from you";
|
||||||
|
"Notification.ProximityYouReached" = "You are now within %1$@ from %2$@";
|
||||||
|
|
||||||
"Location.ProximityNotification.Title" = "Proximity Alert";
|
"Location.ProximityNotification.Title" = "Proximity Alert";
|
||||||
"Location.ProximityNotification.Notify" = "Notify me within %@";
|
"Location.ProximityNotification.Notify" = "Notify me within %@";
|
||||||
@ -5880,6 +5879,11 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"Location.LiveLocationRequired.Description" = "For the alert to work, please share your live location in this chat.";
|
"Location.LiveLocationRequired.Description" = "For the alert to work, please share your live location in this chat.";
|
||||||
"Location.LiveLocationRequired.ShareLocation" = "Share Location";
|
"Location.LiveLocationRequired.ShareLocation" = "Share Location";
|
||||||
|
|
||||||
|
"Location.ProximityAlertSetTitle" = "Proximity alert set";
|
||||||
|
"Location.ProximityAlertSetText" = "We will notify you once %1$@ is within %2$@ from you.";
|
||||||
|
"Location.ProximityAlertSetTextGroup" = "We will notify you once any other group member is within %@ from you.";
|
||||||
|
"Location.ProximityAlertCancelled" = "Proximity alert cancelled";
|
||||||
|
|
||||||
"Stats.Message.Views" = "Views";
|
"Stats.Message.Views" = "Views";
|
||||||
"Stats.Message.PublicShares" = "Public Shares";
|
"Stats.Message.PublicShares" = "Public Shares";
|
||||||
"Stats.Message.PrivateShares" = "Private Shares";
|
"Stats.Message.PrivateShares" = "Private Shares";
|
||||||
|
@ -34,8 +34,11 @@ public final class LiveLocationManagerImpl: LiveLocationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let deviceLocationDisposable = MetaDisposable()
|
private let deviceLocationDisposable = MetaDisposable()
|
||||||
|
private let updateCoordinateDisposable = MetaDisposable()
|
||||||
private var messagesDisposable: Disposable?
|
private var messagesDisposable: Disposable?
|
||||||
|
|
||||||
|
private var deviceLocationPromise = Promise<(CLLocation, Double?)>()
|
||||||
|
|
||||||
private var broadcastToMessageIds: [MessageId: Int32] = [:]
|
private var broadcastToMessageIds: [MessageId: Int32] = [:]
|
||||||
private var stopMessageIds = Set<MessageId>()
|
private var stopMessageIds = Set<MessageId>()
|
||||||
|
|
||||||
@ -106,26 +109,36 @@ public final class LiveLocationManagerImpl: LiveLocationManager {
|
|||||||
|> deliverOn(self.queue)).start(next: { [weak self] value in
|
|> deliverOn(self.queue)).start(next: { [weak self] value in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if value {
|
if value {
|
||||||
let queue = strongSelf.queue
|
strongSelf.deviceLocationDisposable.set(strongSelf.locationManager.push(mode: .precise, updated: { [weak self] location, heading in
|
||||||
strongSelf.deviceLocationDisposable.set(strongSelf.locationManager.push(mode: .precise, updated: { location, heading in
|
self?.deviceLocationPromise.set(.single((location, heading)))
|
||||||
queue.async {
|
|
||||||
var effectiveHeading = heading ?? location.course
|
|
||||||
if location.speed > 1.0 {
|
|
||||||
effectiveHeading = location.course
|
|
||||||
}
|
|
||||||
self?.updateDeviceCoordinate(location.coordinate, accuracyRadius: location.horizontalAccuracy, heading: effectiveHeading)
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
strongSelf.deviceLocationDisposable.set(nil)
|
strongSelf.deviceLocationDisposable.set(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let throttledDeviceLocation = self.deviceLocationPromise.get()
|
||||||
|
|> mapToThrottled { next -> Signal<(CLLocation, Double?), NoError> in
|
||||||
|
return .single(next) |> then(.complete() |> delay(4.0, queue: Queue.concurrentDefaultQueue()))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateCoordinateDisposable.set((throttledDeviceLocation
|
||||||
|
|> deliverOn(self.queue)).start(next: { [weak self] location, heading in
|
||||||
|
if let strongSelf = self {
|
||||||
|
var effectiveHeading = heading ?? location.course
|
||||||
|
if location.speed > 1.0 {
|
||||||
|
effectiveHeading = location.course
|
||||||
|
}
|
||||||
|
strongSelf.updateDeviceCoordinate(location.coordinate, accuracyRadius: location.horizontalAccuracy, heading: effectiveHeading)
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.requiredLocationTypeDisposable?.dispose()
|
self.requiredLocationTypeDisposable?.dispose()
|
||||||
self.deviceLocationDisposable.dispose()
|
self.deviceLocationDisposable.dispose()
|
||||||
|
self.updateCoordinateDisposable.dispose()
|
||||||
self.messagesDisposable?.dispose()
|
self.messagesDisposable?.dispose()
|
||||||
self.editMessageDisposables.dispose()
|
self.editMessageDisposables.dispose()
|
||||||
self.invalidationTimer?.0.invalidate()
|
self.invalidationTimer?.0.invalidate()
|
||||||
|
@ -5,6 +5,7 @@ import Display
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
|
||||||
private let textFont = Font.with(size: 13.0, design: .round, traits: [.bold])
|
private let textFont = Font.with(size: 13.0, design: .round, traits: [.bold])
|
||||||
|
private let smallTextFont = Font.with(size: 11.0, design: .round, traits: [.bold])
|
||||||
|
|
||||||
private class ChatMessageLiveLocationTimerNodeParams: NSObject {
|
private class ChatMessageLiveLocationTimerNodeParams: NSObject {
|
||||||
let backgroundColor: UIColor
|
let backgroundColor: UIColor
|
||||||
@ -119,10 +120,16 @@ public final class ChatMessageLiveLocationTimerNode: ASDisplayNode {
|
|||||||
path.lineCapStyle = .round
|
path.lineCapStyle = .round
|
||||||
path.stroke()
|
path.stroke()
|
||||||
|
|
||||||
let attributes: [NSAttributedString.Key: Any] = [.font: textFont, .foregroundColor: parameters.foregroundColor]
|
let attributes: [NSAttributedString.Key: Any] = [.font: parameters.string.count > 2 ? smallTextFont : textFont, .foregroundColor: parameters.foregroundColor]
|
||||||
let nsString = parameters.string as NSString
|
let nsString = parameters.string as NSString
|
||||||
let size = nsString.size(withAttributes: attributes)
|
let size = nsString.size(withAttributes: attributes)
|
||||||
nsString.draw(at: CGPoint(x: floor((bounds.size.width - size.width) / 2.0), y: floor((bounds.size.height - size.height) / 2.0)), withAttributes: attributes)
|
|
||||||
|
var offset: CGFloat = 0.0
|
||||||
|
if parameters.string.count > 2 {
|
||||||
|
offset = UIScreenPixel
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString.draw(at: CGPoint(x: floor((bounds.size.width - size.width) / 2.0), y: floor((bounds.size.height - size.height) / 2.0) + offset), withAttributes: attributes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ static_library(
|
|||||||
"//submodules/LiveLocationTimerNode:LiveLocationTimerNode",
|
"//submodules/LiveLocationTimerNode:LiveLocationTimerNode",
|
||||||
"//submodules/TelegramNotices:TelegramNotices",
|
"//submodules/TelegramNotices:TelegramNotices",
|
||||||
"//submodules/TooltipUI:TooltipUI",
|
"//submodules/TooltipUI:TooltipUI",
|
||||||
|
"//submodules/UndoUI:UndoUI",
|
||||||
],
|
],
|
||||||
frameworks = [
|
frameworks = [
|
||||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||||
|
@ -39,6 +39,7 @@ swift_library(
|
|||||||
"//submodules/LiveLocationTimerNode:LiveLocationTimerNode",
|
"//submodules/LiveLocationTimerNode:LiveLocationTimerNode",
|
||||||
"//submodules/TelegramNotices:TelegramNotices",
|
"//submodules/TelegramNotices:TelegramNotices",
|
||||||
"//submodules/TooltipUI:TooltipUI",
|
"//submodules/TooltipUI:TooltipUI",
|
||||||
|
"//submodules/UndoUI:UndoUI",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -239,7 +239,10 @@ final class LocationActionListItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
let titleSpacing: CGFloat = 1.0
|
let titleSpacing: CGFloat = 1.0
|
||||||
let bottomInset: CGFloat = hasSeparator ? 0.0 : 4.0
|
let bottomInset: CGFloat = hasSeparator ? 0.0 : 4.0
|
||||||
let contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height + bottomInset)
|
var contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height + bottomInset)
|
||||||
|
if hasSeparator {
|
||||||
|
contentSize.height = max(52.0, contentSize.height)
|
||||||
|
}
|
||||||
let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets())
|
let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets())
|
||||||
|
|
||||||
return (nodeLayout, { [weak self] in
|
return (nodeLayout, { [weak self] in
|
||||||
|
@ -52,6 +52,7 @@ class LocationPinAnnotation: NSObject, MKAnnotation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isSelf = false
|
||||||
var selfPeer: Peer?
|
var selfPeer: Peer?
|
||||||
var title: String? = ""
|
var title: String? = ""
|
||||||
var subtitle: String? = ""
|
var subtitle: String? = ""
|
||||||
@ -78,11 +79,12 @@ class LocationPinAnnotation: NSObject, MKAnnotation {
|
|||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(context: AccountContext, theme: PresentationTheme, message: Message, selfPeer: Peer?, heading: Int32?) {
|
init(context: AccountContext, theme: PresentationTheme, message: Message, selfPeer: Peer?, isSelf: Bool, heading: Int32?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.location = nil
|
self.location = nil
|
||||||
self.peer = nil
|
self.peer = nil
|
||||||
|
self.isSelf = isSelf
|
||||||
self.message = message
|
self.message = message
|
||||||
if let location = getLocation(from: message) {
|
if let location = getLocation(from: message) {
|
||||||
self.coordinate = location.coordinate
|
self.coordinate = location.coordinate
|
||||||
@ -311,6 +313,7 @@ class LocationPinAnnotationView: MKAnnotationView {
|
|||||||
self.smallNode.image = generateSmallBackgroundImage(color: color)
|
self.smallNode.image = generateSmallBackgroundImage(color: color)
|
||||||
self.dotNode.image = generateFilledCircleImage(diameter: 6.0, color: color)
|
self.dotNode.image = generateFilledCircleImage(diameter: 6.0, color: color)
|
||||||
|
|
||||||
|
self.iconNode.isHidden = false
|
||||||
self.dotNode.isHidden = false
|
self.dotNode.isHidden = false
|
||||||
|
|
||||||
if !self.isSelected {
|
if !self.isSelected {
|
||||||
@ -354,6 +357,7 @@ class LocationPinAnnotationView: MKAnnotationView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
|
self.previousPeerId = nil
|
||||||
self.smallNode.isHidden = true
|
self.smallNode.isHidden = true
|
||||||
self.backgroundNode.isHidden = false
|
self.backgroundNode.isHidden = false
|
||||||
self.appeared = false
|
self.appeared = false
|
||||||
|
@ -28,16 +28,18 @@ final class LocationDistancePickerScreen: ViewController {
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let style: LocationDistancePickerScreenStyle
|
private let style: LocationDistancePickerScreenStyle
|
||||||
private let distances: Signal<[Double], NoError>
|
private let distances: Signal<[Double], NoError>
|
||||||
|
private let compactDisplayTitle: String?
|
||||||
private let updated: (Int32?) -> Void
|
private let updated: (Int32?) -> Void
|
||||||
private let completion: (Int32, @escaping () -> Void) -> Void
|
private let completion: (Int32, @escaping () -> Void) -> Void
|
||||||
private let willDismiss: () -> Void
|
private let willDismiss: () -> Void
|
||||||
|
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
init(context: AccountContext, style: LocationDistancePickerScreenStyle, distances: Signal<[Double], NoError>, updated: @escaping (Int32?) -> Void, completion: @escaping (Int32, @escaping () -> Void) -> Void, willDismiss: @escaping () -> Void) {
|
init(context: AccountContext, style: LocationDistancePickerScreenStyle, compactDisplayTitle: String?, distances: Signal<[Double], NoError>, updated: @escaping (Int32?) -> Void, completion: @escaping (Int32, @escaping () -> Void) -> Void, willDismiss: @escaping () -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.style = style
|
self.style = style
|
||||||
self.distances = distances
|
self.distances = distances
|
||||||
|
self.compactDisplayTitle = compactDisplayTitle
|
||||||
self.updated = updated
|
self.updated = updated
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
self.willDismiss = willDismiss
|
self.willDismiss = willDismiss
|
||||||
@ -67,7 +69,7 @@ final class LocationDistancePickerScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = LocationDistancePickerScreenNode(context: self.context, style: self.style, distances: self.distances)
|
self.displayNode = LocationDistancePickerScreenNode(context: self.context, style: self.style, compactDisplayTitle: self.compactDisplayTitle, distances: self.distances)
|
||||||
self.controllerNode.updated = { [weak self] distance in
|
self.controllerNode.updated = { [weak self] distance in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -173,6 +175,7 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let controllerStyle: LocationDistancePickerScreenStyle
|
private let controllerStyle: LocationDistancePickerScreenStyle
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
|
private var compactDisplayTitle: String?
|
||||||
private var distances: [Double] = []
|
private var distances: [Double] = []
|
||||||
|
|
||||||
private let dimNode: ASDisplayNode
|
private let dimNode: ASDisplayNode
|
||||||
@ -186,10 +189,14 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
private let cancelButton: HighlightableButtonNode
|
private let cancelButton: HighlightableButtonNode
|
||||||
private let doneButton: SolidRoundedButtonNode
|
private let doneButton: SolidRoundedButtonNode
|
||||||
|
|
||||||
|
private let measureButtonTitleNode: ImmediateTextNode
|
||||||
|
|
||||||
private var pickerView: TimerPickerView?
|
private var pickerView: TimerPickerView?
|
||||||
private let unitLabelNode: ImmediateTextNode
|
private let unitLabelNode: ImmediateTextNode
|
||||||
private let smallUnitLabelNode: ImmediateTextNode
|
private let smallUnitLabelNode: ImmediateTextNode
|
||||||
|
|
||||||
|
private var pickerTimer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
private var containerLayout: (ContainerViewLayout, CGFloat)?
|
private var containerLayout: (ContainerViewLayout, CGFloat)?
|
||||||
|
|
||||||
private var distancesDisposable: Disposable?
|
private var distancesDisposable: Disposable?
|
||||||
@ -199,10 +206,11 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
var dismiss: (() -> Void)?
|
var dismiss: (() -> Void)?
|
||||||
var cancel: (() -> Void)?
|
var cancel: (() -> Void)?
|
||||||
|
|
||||||
init(context: AccountContext, style: LocationDistancePickerScreenStyle, distances: Signal<[Double], NoError>) {
|
init(context: AccountContext, style: LocationDistancePickerScreenStyle, compactDisplayTitle: String?, distances: Signal<[Double], NoError>) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.controllerStyle = style
|
self.controllerStyle = style
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
self.compactDisplayTitle = compactDisplayTitle
|
||||||
|
|
||||||
self.wrappingScrollNode = ASScrollNode()
|
self.wrappingScrollNode = ASScrollNode()
|
||||||
self.wrappingScrollNode.view.alwaysBounceVertical = true
|
self.wrappingScrollNode.view.alwaysBounceVertical = true
|
||||||
@ -256,9 +264,10 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
self.doneButton.title = self.presentationData.strings.Conversation_Timer_Send
|
self.doneButton.title = self.presentationData.strings.Conversation_Timer_Send
|
||||||
|
|
||||||
self.unitLabelNode = ImmediateTextNode()
|
self.unitLabelNode = ImmediateTextNode()
|
||||||
|
|
||||||
self.smallUnitLabelNode = ImmediateTextNode()
|
self.smallUnitLabelNode = ImmediateTextNode()
|
||||||
|
|
||||||
|
self.measureButtonTitleNode = ImmediateTextNode()
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.backgroundColor = nil
|
self.backgroundColor = nil
|
||||||
@ -314,6 +323,8 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.distancesDisposable?.dispose()
|
self.distancesDisposable?.dispose()
|
||||||
|
|
||||||
|
self.pickerTimer?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupPickerView() {
|
func setupPickerView() {
|
||||||
@ -338,6 +349,18 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
self.contentContainerNode.addSubnode(self.unitLabelNode)
|
self.contentContainerNode.addSubnode(self.unitLabelNode)
|
||||||
self.contentContainerNode.addSubnode(self.smallUnitLabelNode)
|
self.contentContainerNode.addSubnode(self.smallUnitLabelNode)
|
||||||
|
|
||||||
|
self.pickerTimer?.invalidate()
|
||||||
|
|
||||||
|
let pickerTimer = SwiftSignalKit.Timer(timeout: 0.4, repeat: true, completion: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if strongSelf.update() {
|
||||||
|
strongSelf.updateDoneButtonTitle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, queue: Queue.mainQueue())
|
||||||
|
self.pickerTimer = pickerTimer
|
||||||
|
pickerTimer.start()
|
||||||
|
|
||||||
self.updateDoneButtonTitle()
|
self.updateDoneButtonTitle()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,19 +387,38 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
let largeValue = unitValues[selectedLargeRow]
|
let largeValue = unitValues[selectedLargeRow]
|
||||||
let smallValue = smallUnitValues[selectedSmallRow]
|
let smallValue = smallUnitValues[selectedSmallRow]
|
||||||
|
|
||||||
var value = largeValue * 1000 + smallValue * 10
|
let value = largeValue * 1000 + smallValue * 10
|
||||||
if !self.usesMetricSystem {
|
var formattedValue = String(format: "%0.1f", CGFloat(value) / 1000.0)
|
||||||
value = Int32(Double(value) * 1.60934)
|
if smallValue == 5 {
|
||||||
|
formattedValue = formattedValue.replacingOccurrences(of: ".1", with: ".05").replacingOccurrences(of: ",1", with: ",05")
|
||||||
}
|
}
|
||||||
let distance = stringForDistance(strings: self.presentationData.strings, distance: CLLocationDistance(value))
|
let distance = self.usesMetricSystem ? "\(formattedValue) \(self.presentationData.strings.Location_ProximityNotification_DistanceKM)" : "\(formattedValue) \(self.presentationData.strings.Location_ProximityNotification_DistanceMI)"
|
||||||
self.doneButton.title = self.presentationData.strings.Location_ProximityNotification_Notify(distance).0
|
|
||||||
|
let shortTitle = self.presentationData.strings.Location_ProximityNotification_Notify(distance).0
|
||||||
|
var longTitle: String?
|
||||||
|
if let displayTitle = self.compactDisplayTitle, let (layout, _) = self.containerLayout {
|
||||||
|
let title = self.presentationData.strings.Location_ProximityNotification_NotifyLong(displayTitle, distance).0
|
||||||
|
let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: layout.safeInsets.left)
|
||||||
|
|
||||||
|
self.measureButtonTitleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: .black)
|
||||||
|
let titleSize = self.measureButtonTitleNode.updateLayout(CGSize(width: width * 2.0, height: 50.0))
|
||||||
|
if titleSize.width < width - 70.0 {
|
||||||
|
longTitle = title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.doneButton.title = longTitle ?? shortTitle
|
||||||
|
|
||||||
self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Location_ProximityNotification_AlreadyClose(distance).0, font: Font.regular(14.0), textColor: self.presentationData.theme.actionSheet.secondaryTextColor)
|
self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Location_ProximityNotification_AlreadyClose(distance).0, font: Font.regular(14.0), textColor: self.presentationData.theme.actionSheet.secondaryTextColor)
|
||||||
if let (layout, navigationBarHeight) = self.containerLayout {
|
if let (layout, navigationBarHeight) = self.containerLayout {
|
||||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let distance = self.distances.last, Double(value) > distance {
|
var convertedValue = Double(value)
|
||||||
|
if !self.usesMetricSystem {
|
||||||
|
convertedValue = Double(convertedValue) * 1.60934
|
||||||
|
}
|
||||||
|
|
||||||
|
if let distance = self.distances.last, convertedValue > distance {
|
||||||
self.doneButton.alpha = 0.0
|
self.doneButton.alpha = 0.0
|
||||||
self.doneButton.isUserInteractionEnabled = false
|
self.doneButton.isUserInteractionEnabled = false
|
||||||
self.textNode.alpha = 1.0
|
self.textNode.alpha = 1.0
|
||||||
@ -388,27 +430,42 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func update() {
|
var previousReportedValue: Int32?
|
||||||
|
fileprivate func update() -> Bool {
|
||||||
if let pickerView = self.pickerView {
|
if let pickerView = self.pickerView {
|
||||||
let largeValue = unitValues[pickerView.selectedRow(inComponent: 0)]
|
let selectedLargeRow = pickerView.selectedRow(inComponent: 0)
|
||||||
let smallValue = smallUnitValues[pickerView.selectedRow(inComponent: 1)]
|
var selectedSmallRow = pickerView.selectedRow(inComponent: 1)
|
||||||
|
if selectedLargeRow == 0 && selectedSmallRow == 0 {
|
||||||
|
selectedSmallRow = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let largeValue = unitValues[selectedLargeRow]
|
||||||
|
let smallValue = smallUnitValues[selectedSmallRow]
|
||||||
|
|
||||||
var value = largeValue * 1000 + smallValue * 10
|
var value = largeValue * 1000 + smallValue * 10
|
||||||
if !self.usesMetricSystem {
|
if !self.usesMetricSystem {
|
||||||
value = Int32(Double(value) * 1.60934)
|
value = Int32(Double(value) * 1.60934)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let previousReportedValue = self.previousReportedValue, value == previousReportedValue {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
self.updated?(value)
|
self.updated?(value)
|
||||||
|
self.previousReportedValue = value
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
||||||
if pickerView.selectedRow(inComponent: 0) == 0 && pickerView.selectedRow(inComponent: 1) == 0 {
|
if pickerView.selectedRow(inComponent: 0) == 0 && pickerView.selectedRow(inComponent: 1) == 0 {
|
||||||
pickerView.selectRow(1, inComponent: 1, animated: true)
|
pickerView.selectRow(1, inComponent: 1, animated: true)
|
||||||
} else {
|
}
|
||||||
self.updateDoneButtonTitle()
|
self.updateDoneButtonTitle()
|
||||||
self.update()
|
self.update()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||||||
if component == 0 {
|
if component == 0 {
|
||||||
@ -540,6 +597,7 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
|
|
||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
var hadValidLayout = self.containerLayout != nil
|
||||||
self.containerLayout = (layout, navigationBarHeight)
|
self.containerLayout = (layout, navigationBarHeight)
|
||||||
|
|
||||||
var insets = layout.insets(options: [.statusBar, .input])
|
var insets = layout.insets(options: [.statusBar, .input])
|
||||||
@ -595,5 +653,9 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
transition.updateFrame(node: self.smallUnitLabelNode, frame: CGRect(origin: CGPoint(x: floor(pickerFrame.width / 4.0 * 3.0) + 50.0, y: floor(pickerFrame.center.y - smallUnitLabelSize.height / 2.0)), size: smallUnitLabelSize))
|
transition.updateFrame(node: self.smallUnitLabelNode, frame: CGRect(origin: CGPoint(x: floor(pickerFrame.width / 4.0 * 3.0) + 50.0, y: floor(pickerFrame.center.y - smallUnitLabelSize.height / 2.0)), size: smallUnitLabelSize))
|
||||||
|
|
||||||
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame)
|
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame)
|
||||||
|
|
||||||
|
if !hadValidLayout {
|
||||||
|
self.updateDoneButtonTitle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ final class LocationLiveListItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
var subtitle = timeString
|
var subtitle = timeString
|
||||||
if let distance = item.distance {
|
if let distance = item.distance {
|
||||||
let distanceString = item.presentationData.strings.Map_DistanceAway(stringForDistance(strings: item.presentationData.strings, distance: distance)).0
|
let distanceString = item.presentationData.strings.Map_DistanceAway(shortStringForDistance(strings: item.presentationData.strings, distance: Int32(distance))).0
|
||||||
subtitle = "\(timeString) • \(distanceString)"
|
subtitle = "\(timeString) • \(distanceString)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
|||||||
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)
|
self.validLayout = (layout, navigationBarHeight, topPadding, offset, size)
|
||||||
|
|
||||||
let mapHeight: CGFloat = floor(layout.size.height * 1.5)
|
let mapHeight: CGFloat = floor(layout.size.height * 1.3)
|
||||||
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)
|
||||||
|
@ -158,6 +158,8 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
|||||||
private weak var userLocationAnnotationView: MKAnnotationView?
|
private weak var userLocationAnnotationView: MKAnnotationView?
|
||||||
private var headingArrowView: UIImageView?
|
private var headingArrowView: UIImageView?
|
||||||
|
|
||||||
|
private weak var defaultUserLocationAnnotation: MKAnnotation?
|
||||||
|
|
||||||
private let pinDisposable = MetaDisposable()
|
private let pinDisposable = MetaDisposable()
|
||||||
|
|
||||||
private var mapView: LocationMapView? {
|
private var mapView: LocationMapView? {
|
||||||
@ -253,11 +255,16 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
|||||||
self.mapView?.showsUserLocation = true
|
self.mapView?.showsUserLocation = true
|
||||||
self.mapView?.showsPointsOfInterest = false
|
self.mapView?.showsPointsOfInterest = false
|
||||||
self.mapView?.customHitTest = { [weak self] point in
|
self.mapView?.customHitTest = { [weak self] point in
|
||||||
guard let strongSelf = self, let annotationView = strongSelf.customUserLocationAnnotationView else {
|
guard let strongSelf = self else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if let annotationRect = annotationView.superview?.convert(annotationView.frame.insetBy(dx: -16.0, dy: -16.0), to: strongSelf.mapView), annotationRect.contains(point) {
|
if let annotationView = strongSelf.customUserLocationAnnotationView, let annotationRect = annotationView.superview?.convert(annotationView.frame.insetBy(dx: -16.0, dy: -16.0), to: strongSelf.mapView), annotationRect.contains(point) {
|
||||||
|
strongSelf.userLocationAnnotationSelected?()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if let userAnnotation = strongSelf.defaultUserLocationAnnotation, let annotationView = strongSelf.mapView?.view(for: userAnnotation), let annotationRect = annotationView.superview?.convert(annotationView.frame.insetBy(dx: -16.0, dy: -16.0), to: strongSelf.mapView), annotationRect.contains(point) {
|
||||||
strongSelf.userLocationAnnotationSelected?()
|
strongSelf.userLocationAnnotationSelected?()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -390,6 +397,9 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
|||||||
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
|
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
|
||||||
for view in views {
|
for view in views {
|
||||||
if view.annotation is MKUserLocation {
|
if view.annotation is MKUserLocation {
|
||||||
|
self.defaultUserLocationAnnotation = view.annotation
|
||||||
|
view.canShowCallout = false
|
||||||
|
|
||||||
self.userLocationAnnotationView = view
|
self.userLocationAnnotationView = view
|
||||||
if let headingArrowView = self.headingArrowView {
|
if let headingArrowView = self.headingArrowView {
|
||||||
view.addSubview(headingArrowView)
|
view.addSubview(headingArrowView)
|
||||||
@ -471,6 +481,9 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
|||||||
var distances: [Double] = []
|
var distances: [Double] = []
|
||||||
if let userLocation = userLocation {
|
if let userLocation = userLocation {
|
||||||
for annotation in annotations {
|
for annotation in annotations {
|
||||||
|
if annotation.isSelf {
|
||||||
|
continue
|
||||||
|
}
|
||||||
distances.append(userLocation.distance(from: CLLocation(latitude: annotation.coordinate.latitude, longitude: annotation.coordinate.longitude)))
|
distances.append(userLocation.distance(from: CLLocation(latitude: annotation.coordinate.latitude, longitude: annotation.coordinate.longitude)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -483,7 +496,8 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var userLocation: Signal<CLLocation?, NoError> {
|
var userLocation: Signal<CLLocation?, NoError> {
|
||||||
return self.locationPromise.get()
|
return .single(self.currentUserLocation)
|
||||||
|
|> then (self.locationPromise.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
var mapCenterCoordinate: CLLocationCoordinate2D? {
|
var mapCenterCoordinate: CLLocationCoordinate2D? {
|
||||||
@ -693,15 +707,15 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
|||||||
guard let mapView = self.mapView else {
|
guard let mapView = self.mapView else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var annotations: [MKAnnotation] = []
|
var coordinates: [CLLocationCoordinate2D] = []
|
||||||
if let userAnnotation = self.userLocationAnnotation {
|
if let location = self.currentUserLocation {
|
||||||
annotations.append(userAnnotation)
|
coordinates.append(location.coordinate)
|
||||||
}
|
}
|
||||||
annotations.append(contentsOf: self.annotations)
|
coordinates.append(contentsOf: self.annotations.map { $0.coordinate })
|
||||||
|
|
||||||
var zoomRect: MKMapRect?
|
var zoomRect: MKMapRect?
|
||||||
for annotation in annotations {
|
for coordinate in coordinates {
|
||||||
let pointRegionRect = MKMapRect(region: MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 100, longitudinalMeters: 100))
|
let pointRegionRect = MKMapRect(region: MKCoordinateRegion(center: coordinate, latitudinalMeters: 100, longitudinalMeters: 100))
|
||||||
if let currentZoomRect = zoomRect {
|
if let currentZoomRect = zoomRect {
|
||||||
zoomRect = currentZoomRect.union(pointRegionRect)
|
zoomRect = currentZoomRect.union(pointRegionRect)
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,6 +7,7 @@ import SyncCore
|
|||||||
import Postbox
|
import Postbox
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import TelegramStringFormatting
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import AppBundle
|
import AppBundle
|
||||||
import CoreLocation
|
import CoreLocation
|
||||||
@ -14,6 +15,7 @@ import PresentationDataUtils
|
|||||||
import OpenInExternalAppUI
|
import OpenInExternalAppUI
|
||||||
import ShareController
|
import ShareController
|
||||||
import DeviceAccess
|
import DeviceAccess
|
||||||
|
import UndoUI
|
||||||
|
|
||||||
public class LocationViewParams {
|
public class LocationViewParams {
|
||||||
let sendLiveLocation: (TelegramMediaMap) -> Void
|
let sendLiveLocation: (TelegramMediaMap) -> Void
|
||||||
@ -194,12 +196,31 @@ public final class LocationViewController: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Queue.mainQueue().after(0.5) {
|
||||||
strongSelf.controllerNode.updateState { state in
|
strongSelf.controllerNode.updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
state.cancellingProximityRadius = false
|
state.cancellingProximityRadius = false
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
strongSelf.dismissAllTooltips()
|
||||||
|
strongSelf.present(
|
||||||
|
UndoOverlayController(
|
||||||
|
presentationData: strongSelf.presentationData,
|
||||||
|
content: .setProximityAlert(
|
||||||
|
title: strongSelf.presentationData.strings.Location_ProximityAlertCancelled,
|
||||||
|
text: "",
|
||||||
|
cancelled: true
|
||||||
|
),
|
||||||
|
elevatedLayout: false,
|
||||||
|
action: { action in
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
),
|
||||||
|
in: .current
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DeviceAccess.authorizeAccess(to: .location(.live), locationManager: strongSelf.locationManager, presentationData: strongSelf.presentationData, present: { c, a in
|
DeviceAccess.authorizeAccess(to: .location(.live), locationManager: strongSelf.locationManager, presentationData: strongSelf.presentationData, present: { c, a in
|
||||||
@ -212,7 +233,18 @@ public final class LocationViewController: ViewController {
|
|||||||
}
|
}
|
||||||
strongSelf.controllerNode.setProximityIndicator(radius: 0)
|
strongSelf.controllerNode.setProximityIndicator(radius: 0)
|
||||||
|
|
||||||
let controller = LocationDistancePickerScreen(context: context, style: .default, distances: strongSelf.controllerNode.headerNode.mapNode.distancesToAllAnnotations, updated: { [weak self] distance in
|
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.subject.id.peerId)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var compactDisplayTitle: String?
|
||||||
|
if let peer = peer as? TelegramUser {
|
||||||
|
compactDisplayTitle = peer.compactDisplayTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = LocationDistancePickerScreen(context: context, style: .default, compactDisplayTitle: compactDisplayTitle, distances: strongSelf.controllerNode.headerNode.mapNode.distancesToAllAnnotations, updated: { [weak self] distance in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -233,12 +265,39 @@ public final class LocationViewController: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Queue.mainQueue().after(0.5) {
|
||||||
strongSelf.controllerNode.updateState { state in
|
strongSelf.controllerNode.updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
state.updatingProximityRadius = nil
|
state.updatingProximityRadius = nil
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var text: String
|
||||||
|
let distanceString = shortStringForDistance(strings: strongSelf.presentationData.strings, distance: distance)
|
||||||
|
if let compactDisplayTitle = compactDisplayTitle {
|
||||||
|
text = strongSelf.presentationData.strings.Location_ProximityAlertSetText(compactDisplayTitle, distanceString).0
|
||||||
|
} else {
|
||||||
|
text = strongSelf.presentationData.strings.Location_ProximityAlertSetTextGroup(distanceString).0
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.dismissAllTooltips()
|
||||||
|
strongSelf.present(
|
||||||
|
UndoOverlayController(
|
||||||
|
presentationData: strongSelf.presentationData,
|
||||||
|
content: .setProximityAlert(
|
||||||
|
title: strongSelf.presentationData.strings.Location_ProximityAlertSetTitle,
|
||||||
|
text: text,
|
||||||
|
cancelled: false
|
||||||
|
),
|
||||||
|
elevatedLayout: false,
|
||||||
|
action: { action in
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
),
|
||||||
|
in: .current
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_Title, text: strongSelf.presentationData.strings.Location_LiveLocationRequired_Description, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_ShareLocation, action: {
|
strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_Title, text: strongSelf.presentationData.strings.Location_LiveLocationRequired_Description, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_ShareLocation, action: {
|
||||||
completion()
|
completion()
|
||||||
@ -252,6 +311,7 @@ public final class LocationViewController: ViewController {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
strongSelf.present(controller, in: .window(.root))
|
strongSelf.present(controller, in: .window(.root))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, updateSendActionHighlight: { [weak self] highlighted in
|
}, updateSendActionHighlight: { [weak self] highlighted in
|
||||||
@ -277,6 +337,43 @@ public final class LocationViewController: ViewController {
|
|||||||
|> deliverOnMainQueue).start(next: { coordinate in
|
|> deliverOnMainQueue).start(next: { coordinate in
|
||||||
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: 30 * 60, proximityNotificationRadius: distance))
|
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: 30 * 60, proximityNotificationRadius: distance))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.subject.id.peerId)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var compactDisplayTitle: String?
|
||||||
|
if let peer = peer as? TelegramUser {
|
||||||
|
compactDisplayTitle = peer.compactDisplayTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
var text: String
|
||||||
|
let distanceString = shortStringForDistance(strings: strongSelf.presentationData.strings, distance: distance)
|
||||||
|
if let compactDisplayTitle = compactDisplayTitle {
|
||||||
|
text = strongSelf.presentationData.strings.Location_ProximityAlertSetText(compactDisplayTitle, distanceString).0
|
||||||
|
} else {
|
||||||
|
text = strongSelf.presentationData.strings.Location_ProximityAlertSetTextGroup(distanceString).0
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.dismissAllTooltips()
|
||||||
|
strongSelf.present(
|
||||||
|
UndoOverlayController(
|
||||||
|
presentationData: strongSelf.presentationData,
|
||||||
|
content: .setProximityAlert(
|
||||||
|
title: strongSelf.presentationData.strings.Location_ProximityAlertSetTitle,
|
||||||
|
text: text,
|
||||||
|
cancelled: false
|
||||||
|
),
|
||||||
|
elevatedLayout: false,
|
||||||
|
action: { action in
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
),
|
||||||
|
in: .current
|
||||||
|
)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
let _ = (context.account.postbox.loadedPeerWithId(subject.id.peerId)
|
let _ = (context.account.postbox.loadedPeerWithId(subject.id.peerId)
|
||||||
|> deliverOnMainQueue).start(next: { peer in
|
|> deliverOnMainQueue).start(next: { peer in
|
||||||
@ -293,6 +390,8 @@ public final class LocationViewController: ViewController {
|
|||||||
|> deliverOnMainQueue).start(next: { coordinate in
|
|> deliverOnMainQueue).start(next: { coordinate in
|
||||||
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: period))
|
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: period))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
strongSelf.controllerNode.showAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.setItemGroups([
|
controller.setItemGroups([
|
||||||
@ -355,6 +454,15 @@ public final class LocationViewController: ViewController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func dismissAllTooltips() {
|
||||||
|
self.forEachController({ controller in
|
||||||
|
if let controller = controller as? UndoOverlayController {
|
||||||
|
controller.dismissWithCommitAction()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
super.loadDisplayNode()
|
super.loadDisplayNode()
|
||||||
guard let interaction = self.interaction else {
|
guard let interaction = self.interaction else {
|
||||||
|
@ -48,7 +48,7 @@ private enum LocationViewEntryId: Hashable {
|
|||||||
|
|
||||||
private enum LocationViewEntry: Comparable, Identifiable {
|
private enum LocationViewEntry: Comparable, Identifiable {
|
||||||
case info(PresentationTheme, TelegramMediaMap, String?, Double?, Double?)
|
case info(PresentationTheme, TelegramMediaMap, String?, Double?, Double?)
|
||||||
case toggleLiveLocation(PresentationTheme, String, String, CLLocationCoordinate2D?, Double?, Double?)
|
case toggleLiveLocation(PresentationTheme, String, String, Double?, Double?)
|
||||||
case liveLocation(PresentationTheme, PresentationDateTimeFormat, PresentationPersonNameOrder, Message, Double?, Int)
|
case liveLocation(PresentationTheme, PresentationDateTimeFormat, PresentationPersonNameOrder, Message, Double?, Int)
|
||||||
|
|
||||||
var stableId: LocationViewEntryId {
|
var stableId: LocationViewEntryId {
|
||||||
@ -70,8 +70,8 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .toggleLiveLocation(lhsTheme, lhsTitle, lhsSubtitle, lhsCoordinate, lhsBeginTimestamp, lhsTimeout):
|
case let .toggleLiveLocation(lhsTheme, lhsTitle, lhsSubtitle, lhsBeginTimestamp, lhsTimeout):
|
||||||
if case let .toggleLiveLocation(rhsTheme, rhsTitle, rhsSubtitle, rhsCoordinate, rhsBeginTimestamp, rhsTimeout) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsSubtitle == rhsSubtitle, lhsCoordinate == rhsCoordinate, lhsBeginTimestamp == rhsBeginTimestamp, lhsTimeout == rhsTimeout {
|
if case let .toggleLiveLocation(rhsTheme, rhsTitle, rhsSubtitle, rhsBeginTimestamp, rhsTimeout) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsSubtitle == rhsSubtitle, lhsBeginTimestamp == rhsBeginTimestamp, lhsTimeout == rhsTimeout {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -132,7 +132,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
|||||||
}, getDirections: {
|
}, getDirections: {
|
||||||
interaction?.requestDirections()
|
interaction?.requestDirections()
|
||||||
})
|
})
|
||||||
case let .toggleLiveLocation(_, title, subtitle, coordinate, beginTimstamp, timeout):
|
case let .toggleLiveLocation(_, title, subtitle, beginTimstamp, timeout):
|
||||||
let beginTimeAndTimeout: (Double, Double)?
|
let beginTimeAndTimeout: (Double, Double)?
|
||||||
if let beginTimstamp = beginTimstamp, let timeout = timeout {
|
if let beginTimstamp = beginTimstamp, let timeout = timeout {
|
||||||
beginTimeAndTimeout = (beginTimstamp, timeout)
|
beginTimeAndTimeout = (beginTimstamp, timeout)
|
||||||
@ -149,7 +149,11 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
|||||||
interaction?.updateSendActionHighlight(highlight)
|
interaction?.updateSendActionHighlight(highlight)
|
||||||
})
|
})
|
||||||
case let .liveLocation(_, dateTimeFormat, nameDisplayOrder, message, distance, _):
|
case let .liveLocation(_, dateTimeFormat, nameDisplayOrder, message, distance, _):
|
||||||
return LocationLiveListItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: context, message: message, distance: distance, action: {}, longTapAction: {})
|
return LocationLiveListItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: context, message: message, distance: distance, action: {
|
||||||
|
if let location = getLocation(from: message) {
|
||||||
|
interaction?.goToCoordinate(location.coordinate)
|
||||||
|
}
|
||||||
|
}, longTapAction: {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,10 +281,10 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
setupProximityNotificationImpl = { [weak self] reset in
|
setupProximityNotificationImpl = { reset in
|
||||||
let _ = (liveLocations
|
let _ = (liveLocations
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] messages in
|
|> deliverOnMainQueue).start(next: { messages in
|
||||||
var ownMessageId: MessageId?
|
var ownMessageId: MessageId?
|
||||||
for message in messages {
|
for message in messages {
|
||||||
if message.localTags.contains(.OutgoingLiveLocation) {
|
if message.localTags.contains(.OutgoingLiveLocation) {
|
||||||
@ -369,7 +373,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
|
|
||||||
if let channel = subject.author as? TelegramChannel, case .broadcast = channel.info, activeOwnLiveLocation == nil {
|
if let channel = subject.author as? TelegramChannel, case .broadcast = channel.info, activeOwnLiveLocation == nil {
|
||||||
} else {
|
} else {
|
||||||
entries.append(.toggleLiveLocation(presentationData.theme, title, subtitle, userLocation?.coordinate, beginTime, timeout))
|
entries.append(.toggleLiveLocation(presentationData.theme, title, subtitle, beginTime, timeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
var sortedLiveLocations: [Message] = []
|
var sortedLiveLocations: [Message] = []
|
||||||
@ -409,9 +413,9 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
let distance = userLocation.flatMap { subjectLocation.distance(from: $0) }
|
let distance = userLocation.flatMap { subjectLocation.distance(from: $0) }
|
||||||
|
|
||||||
if message.localTags.contains(.OutgoingLiveLocation), let selfPeer = selfPeer {
|
if message.localTags.contains(.OutgoingLiveLocation), let selfPeer = selfPeer {
|
||||||
userAnnotation = LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading)
|
userAnnotation = LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, isSelf: true, heading: location.heading)
|
||||||
} else {
|
} else {
|
||||||
annotations.append(LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading))
|
annotations.append(LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, isSelf: message.author?.id == context.account.peerId, heading: location.heading))
|
||||||
entries.append(.liveLocation(presentationData.theme, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, message, distance, index))
|
entries.append(.liveLocation(presentationData.theme, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, message, distance, index))
|
||||||
}
|
}
|
||||||
index += 1
|
index += 1
|
||||||
@ -420,6 +424,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
|
|
||||||
if let currentProximityNotification = proximityNotification, currentProximityNotification && state.cancellingProximityRadius {
|
if let currentProximityNotification = proximityNotification, currentProximityNotification && state.cancellingProximityRadius {
|
||||||
proximityNotification = false
|
proximityNotification = false
|
||||||
|
proximityNotificationRadius = nil
|
||||||
} else if let radius = state.updatingProximityRadius {
|
} else if let radius = state.updatingProximityRadius {
|
||||||
proximityNotification = true
|
proximityNotification = true
|
||||||
proximityNotificationRadius = radius
|
proximityNotificationRadius = radius
|
||||||
@ -481,9 +486,11 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
|
|
||||||
if !strongSelf.reportedAnnotationsReady {
|
if !strongSelf.reportedAnnotationsReady {
|
||||||
strongSelf.reportedAnnotationsReady = true
|
strongSelf.reportedAnnotationsReady = true
|
||||||
|
if annotations.count > 0 {
|
||||||
strongSelf.onAnnotationsReady?()
|
strongSelf.onAnnotationsReady?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let _ = proximityNotification {
|
if let _ = proximityNotification {
|
||||||
strongSelf.headerNode.mapNode.activeProximityRadius = proximityNotificationRadius.flatMap { Double($0) }
|
strongSelf.headerNode.mapNode.activeProximityRadius = proximityNotificationRadius.flatMap { Double($0) }
|
||||||
@ -492,7 +499,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
}
|
}
|
||||||
let rightBarButtonAction: LocationViewRightBarButton
|
let rightBarButtonAction: LocationViewRightBarButton
|
||||||
if location.liveBroadcastingTimeout != nil {
|
if location.liveBroadcastingTimeout != nil {
|
||||||
if liveLocations.count > 1 {
|
if annotations.count > 0 {
|
||||||
rightBarButtonAction = .showAll
|
rightBarButtonAction = .showAll
|
||||||
} else {
|
} else {
|
||||||
rightBarButtonAction = .none
|
rightBarButtonAction = .none
|
||||||
@ -551,6 +558,21 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.headerNode.mapNode.annotationSelected = { [weak self] annotation in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let annotation = annotation {
|
||||||
|
strongSelf.interaction.goToCoordinate(annotation.coordinate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.headerNode.mapNode.userLocationAnnotationSelected = { [weak self] in
|
||||||
|
if let strongSelf = self, let location = strongSelf.headerNode.mapNode.currentUserLocation {
|
||||||
|
strongSelf.interaction.goToCoordinate(location.coordinate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.locationManager.manager.startUpdatingHeading()
|
self.locationManager.manager.startUpdatingHeading()
|
||||||
self.locationManager.manager.delegate = self
|
self.locationManager.manager.delegate = self
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,8 @@ private func presentLiveLocationController(context: AccountContext, peerId: Peer
|
|||||||
}, openUrl: { _ in
|
}, openUrl: { _ in
|
||||||
}, openPeer: { peer, navigation in
|
}, openPeer: { peer, navigation in
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { message in
|
||||||
|
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||||
}, sendSticker: nil,
|
}, sendSticker: nil,
|
||||||
setupTemporaryHiddenMedia: { _, _, _ in
|
setupTemporaryHiddenMedia: { _, _, _ in
|
||||||
}, chatAvatarHiddenMedia: { _, _ in
|
}, chatAvatarHiddenMedia: { _, _ in
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -444,7 +444,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
attributedString = nil
|
attributedString = nil
|
||||||
case let .geoProximityReached(fromId, toId, distance):
|
case let .geoProximityReached(fromId, toId, distance):
|
||||||
let distanceString = stringForDistance(strings: strings, distance: Double(distance))
|
let distanceString = stringForDistance(strings: strings, distance: Double(distance))
|
||||||
if toId == accountPeerId {
|
if fromId == accountPeerId {
|
||||||
|
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityYouReached(distanceString, message.peers[toId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? ""), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(1, toId)]))
|
||||||
|
} else if toId == accountPeerId {
|
||||||
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReachedYou(message.peers[fromId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "", distanceString), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, fromId)]))
|
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReachedYou(message.peers[fromId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "", distanceString), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, fromId)]))
|
||||||
} else {
|
} else {
|
||||||
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReached(message.peers[fromId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "", distanceString, message.peers[toId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? ""), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, fromId), (2, toId)]))
|
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReached(message.peers[fromId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "", distanceString, message.peers[toId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? ""), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, fromId), (2, toId)]))
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -829,7 +829,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
wasCheck = true
|
wasCheck = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if isAudio && !isVoice {
|
if isAudio && !isVoice && !isSending {
|
||||||
state = .play
|
state = .play
|
||||||
} else {
|
} else {
|
||||||
if message.groupingKey != nil, adjustedProgress.isEqual(to: 1.0), (message.flags.contains(.Unsent) || wasCheck) {
|
if message.groupingKey != nil, adjustedProgress.isEqual(to: 1.0), (message.flags.contains(.Unsent) || wasCheck) {
|
||||||
|
@ -21,6 +21,7 @@ public enum UndoOverlayContent {
|
|||||||
case chatAddedToFolder(chatTitle: String, folderTitle: String)
|
case chatAddedToFolder(chatTitle: String, folderTitle: String)
|
||||||
case chatRemovedFromFolder(chatTitle: String, folderTitle: String)
|
case chatRemovedFromFolder(chatTitle: String, folderTitle: String)
|
||||||
case messagesUnpinned(title: String, text: String, undo: Bool, isHidden: Bool)
|
case messagesUnpinned(title: String, text: String, undo: Bool, isHidden: Bool)
|
||||||
|
case setProximityAlert(title: String, text: String, cancelled: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum UndoOverlayAction {
|
public enum UndoOverlayAction {
|
||||||
|
@ -221,7 +221,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
displayUndo = undo
|
displayUndo = undo
|
||||||
self.originalRemainingSeconds = undo ? 5 : 5
|
self.originalRemainingSeconds = 5
|
||||||
case let .emoji(path, text):
|
case let .emoji(path, text):
|
||||||
self.iconNode = nil
|
self.iconNode = nil
|
||||||
self.iconCheckNode = nil
|
self.iconCheckNode = nil
|
||||||
@ -388,6 +388,23 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
case let .setProximityAlert(title, text, cancelled):
|
||||||
|
self.iconNode = nil
|
||||||
|
self.iconCheckNode = nil
|
||||||
|
self.animationNode = AnimationNode(animation: cancelled ? "anim_proximity_cancelled" : "anim_proximity_set", colors: [:], scale: 0.45)
|
||||||
|
self.animatedStickerNode = nil
|
||||||
|
|
||||||
|
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||||
|
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||||
|
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
|
||||||
|
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
||||||
|
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
|
||||||
|
if !text.isEmpty {
|
||||||
|
self.textNode.attributedText = attributedText
|
||||||
|
}
|
||||||
|
|
||||||
|
displayUndo = false
|
||||||
|
self.originalRemainingSeconds = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
self.remainingSeconds = self.originalRemainingSeconds
|
self.remainingSeconds = self.originalRemainingSeconds
|
||||||
@ -416,7 +433,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
switch content {
|
switch content {
|
||||||
case .removedChat:
|
case .removedChat:
|
||||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||||
case .archivedChat, .hidArchive, .revealedArchive, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned:
|
case .archivedChat, .hidArchive, .revealedArchive, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert:
|
||||||
break
|
break
|
||||||
case .dice:
|
case .dice:
|
||||||
self.panelWrapperNode.clipsToBounds = true
|
self.panelWrapperNode.clipsToBounds = true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user