Swiftgram/submodules/LocationUI/Sources/LegacyLocationController.swift

282 lines
14 KiB
Swift

import Foundation
import UIKit
import Display
import LegacyComponents
import TelegramCore
import Postbox
import TelegramPresentationData
import AccountContext
import ShareController
import LegacyUI
import OpenInExternalAppUI
import AppBundle
private func generateClearIcon(color: UIColor) -> UIImage? {
return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: color)
}
func makeLegacyPeer(_ peer: Peer) -> AnyObject? {
if let user = peer as? TelegramUser {
let legacyUser = TGUser()
legacyUser.uid = user.id.id
legacyUser.firstName = user.firstName
legacyUser.lastName = user.lastName
if let representation = smallestImageRepresentation(user.photo) {
legacyUser.photoUrlSmall = legacyImageLocationUri(resource: representation.resource)
}
return legacyUser
} else if let channel = peer as? TelegramChannel {
let legacyConversation = TGConversation()
legacyConversation.conversationId = Int64(channel.id.id)
legacyConversation.chatTitle = channel.title
if let representation = smallestImageRepresentation(channel.photo) {
legacyConversation.chatPhotoSmall = legacyImageLocationUri(resource: representation.resource)
}
return legacyConversation
} else {
return nil
}
}
private func makeLegacyMessage(_ message: Message) -> TGMessage {
let result = TGMessage()
result.mid = message.id.id
result.date = Double(message.timestamp)
if message.flags.contains(.Failed) {
result.deliveryState = TGMessageDeliveryStateFailed
} else if message.flags.contains(.Sending) {
result.deliveryState = TGMessageDeliveryStatePending
} else {
result.deliveryState = TGMessageDeliveryStateDelivered
}
for attribute in message.attributes {
if let attribute = attribute as? EditedMessageAttribute {
result.editDate = Double(attribute.date)
}
}
var media: [Any] = []
for m in message.media {
if let mapMedia = m as? TelegramMediaMap {
let legacyLocation = TGLocationMediaAttachment()
legacyLocation.latitude = mapMedia.latitude
legacyLocation.longitude = mapMedia.longitude
if let venue = mapMedia.venue {
legacyLocation.venue = TGVenueAttachment(title: venue.title, address: venue.address, provider: venue.provider, venueId: venue.id, type: venue.type)
}
if let liveBroadcastingTimeout = mapMedia.liveBroadcastingTimeout {
legacyLocation.period = liveBroadcastingTimeout
}
media.append(legacyLocation)
}
}
if !media.isEmpty {
result.mediaAttachments = media
}
return result
}
private func legacyRemainingTime(message: TGMessage) -> SSignal {
var liveBroadcastingTimeoutValue: Int32?
if let mediaAttachments = message.mediaAttachments {
for media in mediaAttachments {
if let m = media as? TGLocationMediaAttachment, m.period != 0 {
liveBroadcastingTimeoutValue = m.period
}
}
}
guard let liveBroadcastingTimeout = liveBroadcastingTimeoutValue else {
return SSignal.fail(nil)
}
if message.deliveryState != TGMessageDeliveryStateDelivered {
return SSignal.single(liveBroadcastingTimeout as NSNumber)
}
let remainingTime = SSignal.`defer`({
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let remainingTime = max(0, Int32(message.date) + liveBroadcastingTimeout - currentTime)
var signal = SSignal.single(remainingTime as NSNumber)
if remainingTime == 0 {
signal = signal?.then(SSignal.fail(nil))
}
return signal
})!
return (remainingTime.then(SSignal.complete().delay(5.0, on: SQueue.main()))).restart().`catch`({ _ in
return SSignal.complete()
})
}
private func telegramMap(for location: TGLocationMediaAttachment) -> TelegramMediaMap {
let mapVenue: MapVenue?
if let venue = location.venue {
mapVenue = MapVenue(title: venue.title ?? "", address: venue.address ?? "", provider: venue.provider ?? "", id: venue.venueId ?? "", type: venue.type ?? "")
} else {
mapVenue = nil
}
return TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, geoPlace: nil, venue: mapVenue, liveBroadcastingTimeout: nil)
}
func legacyLocationPalette(from theme: PresentationTheme) -> TGLocationPallete {
let listTheme = theme.list
let searchTheme = theme.rootController.navigationSearchBar
return TGLocationPallete(backgroundColor: listTheme.plainBackgroundColor, selectionColor: listTheme.itemHighlightedBackgroundColor, separatorColor: listTheme.itemPlainSeparatorColor, textColor: listTheme.itemPrimaryTextColor, secondaryTextColor: listTheme.itemSecondaryTextColor, accentColor: listTheme.itemAccentColor, destructiveColor: listTheme.itemDestructiveColor, locationColor: UIColor(rgb: 0x008df2), liveLocationColor: UIColor(rgb: 0xff6464), iconColor: searchTheme.backgroundColor, sectionHeaderBackgroundColor: theme.chatList.sectionHeaderFillColor, sectionHeaderTextColor: theme.chatList.sectionHeaderTextColor, searchBarPallete: TGSearchBarPallete(dark: theme.overallDarkAppearance, backgroundColor: searchTheme.backgroundColor, highContrastBackgroundColor: searchTheme.backgroundColor, textColor: searchTheme.inputTextColor, placeholderColor: searchTheme.inputPlaceholderTextColor, clearIcon: generateClearIcon(color: theme.rootController.navigationSearchBar.inputClearButtonColor), barBackgroundColor: searchTheme.backgroundColor, barSeparatorColor: searchTheme.separatorColor, plainBackgroundColor: searchTheme.backgroundColor, accentColor: searchTheme.accentColor, accentContrastColor: searchTheme.backgroundColor, menuBackgroundColor: searchTheme.backgroundColor, segmentedControlBackgroundImage: nil, segmentedControlSelectedImage: nil, segmentedControlHighlightedImage: nil, segmentedControlDividerImage: nil), avatarPlaceholder: nil)
}
public func legacyLocationController(message: Message?, mapMedia: TelegramMediaMap, context: AccountContext, openPeer: @escaping (Peer) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, stopLiveLocation: @escaping () -> Void, openUrl: @escaping (String) -> Void) -> ViewController {
let legacyLocation = TGLocationMediaAttachment()
legacyLocation.latitude = mapMedia.latitude
legacyLocation.longitude = mapMedia.longitude
if let venue = mapMedia.venue {
legacyLocation.venue = TGVenueAttachment(title: venue.title, address: venue.address, provider: venue.provider, venueId: venue.id, type: venue.type)
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let legacyController = LegacyController(presentation: .navigation, theme: presentationData.theme, strings: presentationData.strings)
legacyController.navigationPresentation = .modal
let controller: TGLocationViewController
if let message = message {
let legacyMessage = makeLegacyMessage(message)
let legacyAuthor: AnyObject? = message.author.flatMap(makeLegacyPeer)
let updatedLocations = SSignal(generator: { subscriber in
let disposable = topPeerActiveLiveLocationMessages(viewTracker: context.account.viewTracker, accountPeerId: context.account.peerId, peerId: message.id.peerId).start(next: { (_, messages) in
var result: [Any] = []
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
loop: for message in messages {
var liveBroadcastingTimeout: Int32 = 0
mediaLoop: for media in message.media {
if let map = media as? TelegramMediaMap, let timeout = map.liveBroadcastingTimeout {
liveBroadcastingTimeout = timeout
break mediaLoop
}
}
let legacyMessage = makeLegacyMessage(message)
guard let legacyAuthor = message.author.flatMap(makeLegacyPeer) else {
continue loop
}
let remainingTime = max(0, message.timestamp + liveBroadcastingTimeout - currentTime)
if legacyMessage.locationAttachment?.period != 0 {
let hasOwnSession = message.localTags.contains(.OutgoingLiveLocation)
var isOwn = false
if !message.flags.contains(.Incoming) {
isOwn = true
} else if let peer = message.peers[message.id.peerId] as? TelegramChannel {
isOwn = peer.hasPermission(.sendMessages)
}
let liveLocation = TGLiveLocation(message: legacyMessage, peer: legacyAuthor, hasOwnSession: hasOwnSession, isOwnLocation: isOwn, isExpired: remainingTime == 0)!
result.append(liveLocation)
}
}
subscriber?.putNext(result)
})
return SBlockDisposable(block: {
disposable.dispose()
})
})!
if let liveBroadcastingTimeout = mapMedia.liveBroadcastingTimeout {
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let remainingTime = max(0, message.timestamp + liveBroadcastingTimeout - currentTime)
let messageLiveLocation = TGLiveLocation(message: legacyMessage, peer: legacyAuthor, hasOwnSession: false, isOwnLocation: false, isExpired: remainingTime == 0)!
controller = TGLocationViewController(context: legacyController.context, liveLocation: messageLiveLocation)
if remainingTime == 0 {
let freezeLocations: [Any] = [messageLiveLocation]
controller.setLiveLocationsSignal(.single(freezeLocations))
} else {
controller.setLiveLocationsSignal(updatedLocations)
}
} else {
controller = TGLocationViewController(context: legacyController.context, message: legacyMessage, peer: legacyAuthor)!
controller.receivingPeer = message.peers[message.id.peerId].flatMap(makeLegacyPeer)
controller.setLiveLocationsSignal(updatedLocations)
}
let namespacesWithEnabledLiveLocation: Set<PeerId.Namespace> = Set([
Namespaces.Peer.CloudChannel,
Namespaces.Peer.CloudGroup,
Namespaces.Peer.CloudUser
])
if namespacesWithEnabledLiveLocation.contains(message.id.peerId.namespace) {
controller.allowLiveLocationSharing = true
}
} else {
let attachment = TGLocationMediaAttachment()
attachment.latitude = mapMedia.latitude
attachment.longitude = mapMedia.longitude
controller = TGLocationViewController(context: legacyController.context, locationAttachment: attachment, peer: nil)
}
controller.remainingTimeForMessage = { message in
return legacyRemainingTime(message: message!)
}
controller.liveLocationStarted = { [weak legacyController] coordinate, period in
sendLiveLocation(coordinate, period)
legacyController?.dismiss()
}
controller.liveLocationStopped = { [weak legacyController] in
stopLiveLocation()
legacyController?.dismiss()
}
let theme = (context.sharedContext.currentPresentationData.with { $0 }).theme
controller.pallete = legacyLocationPalette(from: theme)
controller.modalMode = true
controller.presentActionsMenu = { [weak legacyController] legacyLocation, directions in
if let strongLegacyController = legacyController, let location = legacyLocation {
let map = telegramMap(for: location)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let shareAction = OpenInControllerAction(title: presentationData.strings.Conversation_ContextMenuShare, action: {
strongLegacyController.present(ShareController(context: context, subject: .mapMedia(map), externalShare: true), in: .window(.root), with: nil)
})
strongLegacyController.present(OpenInActionSheetController(context: context, item: .location(location: map, withDirections: directions), additionalAction: !directions ? shareAction : nil, openUrl: openUrl), in: .window(.root), with: nil)
}
}
controller.onViewDidAppear = { [weak controller] in
if let strongController = controller {
strongController.locationMapView.interactiveTransitionGestureRecognizerTest = { point -> Bool in
return point.x > 30.0
}
}
}
legacyController.navigationItem.title = controller.navigationItem.title
controller.updateRightBarItem = { item, action, animated in
if action {
legacyController.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationShareIcon(theme), style: .plain, target: controller, action: #selector(controller.actionsButtonPressed))
} else {
legacyController.navigationItem.setRightBarButton(item, animated: animated)
}
}
legacyController.bind(controller: controller)
controller.view.disablesInteractiveModalDismiss = true
controller.view.disablesInteractiveTransitionGestureRecognizer = true
let presentationDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in
if let controller = controller {
controller.pallete = legacyLocationPalette(from: presentationData.theme)
}
})
legacyController.disposables.add(presentationDisposable)
return legacyController
}