Skip to content

Commit

Permalink
add a map
Browse files Browse the repository at this point in the history
when experimental 'location streaming' is enabled
the map is available from profiles and from 'all media',
showing positions, POIs and recent tracks
  • Loading branch information
r10s committed Apr 5, 2024
1 parent 71a4a27 commit 96c50d9
Show file tree
Hide file tree
Showing 13 changed files with 286 additions and 11 deletions.
17 changes: 17 additions & 0 deletions DcCore/DcCore/DC/events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public let eventEphemeralTimerModified = Notification.Name(rawValue: "eventEphe
public let eventMsgsNoticed = Notification.Name(rawValue: "eventMsgsNoticed")
public let eventConnectivityChanged = Notification.Name(rawValue: "eventConnectivityChanged")
public let eventWebxdcStatusUpdate = Notification.Name(rawValue: "eventWebxdcStatusUpdate")
public let eventLocationChanged = Notification.Name(rawValue: "eventLocationChanged")

public class DcEventHandler {
let dcAccounts: DcAccounts
Expand Down Expand Up @@ -174,6 +175,22 @@ public class DcEventHandler {
])
}

case DC_EVENT_LOCATION_CHANGED:
if accountId != dcAccounts.getSelected().id {
return
}
logger.info("📡[\(accountId)] location changed for contact \(data1)")
DispatchQueue.main.async {
let nc = NotificationCenter.default
nc.post(
name: eventLocationChanged,
object: nil,
userInfo: [
"contact_id": Int(data1),
]
)
}

default:
break
}
Expand Down
12 changes: 12 additions & 0 deletions DcCore/DcCore/Extensions/UIColor+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,16 @@ public extension UIColor {
return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: 1)
}

var hexValue: String {
var color = self
if color.cgColor.numberOfComponents < 4 {
let c = color.cgColor.components!
color = UIColor(red: c[0], green: c[0], blue: c[0], alpha: c[1])
}
if color.cgColor.colorSpace!.model != .rgb {
return "#FFFFFF"
}
let c = color.cgColor.components!
return String(format: "#%02X%02X%02X", Int(c[0]*255.0), Int(c[1]*255.0), Int(c[2]*255.0))
}
}
4 changes: 4 additions & 0 deletions deltachat-ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
AEFBE22F23FEF23D0045327A /* ProviderInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEFBE22E23FEF23D0045327A /* ProviderInfoCell.swift */; };
B20462E42440A4A600367A57 /* AutodelOverviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20462E32440A4A600367A57 /* AutodelOverviewViewController.swift */; };
B20462E62440C99600367A57 /* AutodelOptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20462E52440C99600367A57 /* AutodelOptionsViewController.swift */; };
B206C2AB2B7B8088003ACBE6 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B206C2AA2B7B8088003ACBE6 /* MapViewController.swift */; };
B21005DB23383664004C70C5 /* EmailOptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21005DA23383664004C70C5 /* EmailOptionsViewController.swift */; };
B2172F3C29C125F2002C289E /* AdvancedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2172F3B29C125F2002C289E /* AdvancedViewController.swift */; };
B259D64329B771D5008FB706 /* BackupTransferViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B259D64229B771D5008FB706 /* BackupTransferViewController.swift */; };
Expand Down Expand Up @@ -499,6 +500,7 @@
B20462E22440805C00367A57 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
B20462E32440A4A600367A57 /* AutodelOverviewViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutodelOverviewViewController.swift; sourceTree = "<group>"; };
B20462E52440C99600367A57 /* AutodelOptionsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutodelOptionsViewController.swift; sourceTree = "<group>"; };
B206C2AA2B7B8088003ACBE6 /* MapViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = "<group>"; };
B209B2042B10139700FBBECF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
B209B2052B10139700FBBECF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
B209B2062B10139700FBBECF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
Expand Down Expand Up @@ -961,6 +963,7 @@
AEE6EC3E2282C59C00EDC689 /* GroupMembersViewController.swift */,
302E592326A5CF4800DD4F58 /* ConnectivityViewController.swift */,
AE19887423EB264000B4CD5F /* HelpViewController.swift */,
B206C2AA2B7B8088003ACBE6 /* MapViewController.swift */,
304F769425DD237B0094B5E2 /* WebViewViewController.swift */,
304F769825DD23D70094B5E2 /* FullMessageViewController.swift */,
305501732798CDE1008FD5CA /* WebxdcViewController.swift */,
Expand Down Expand Up @@ -1577,6 +1580,7 @@
B2C42570265C325C00B95377 /* MultilineLabelCell.swift in Sources */,
AEF53BD5248904BF00D309C1 /* GalleryTimeLabel.swift in Sources */,
AEE6EC482283045D00EDC689 /* SelfProfileViewController.swift in Sources */,
B206C2AB2B7B8088003ACBE6 /* MapViewController.swift in Sources */,
30653081254358B10093E196 /* QuoteView.swift in Sources */,
3067AAC62667F3FE00525036 /* ImageFormat.swift in Sources */,
30EF7308252F6A3300E2C54A /* PaddingTextView.swift in Sources */,
Expand Down
Binary file added deltachat-ios/Assets/maps.xdc
Binary file not shown.
17 changes: 17 additions & 0 deletions deltachat-ios/Controller/AllMediaViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ class AllMediaViewController: UIPageViewController {
return control
}()

private lazy var mapButton: UIBarButtonItem = {
if #available(iOS 13.0, *) {
return UIBarButtonItem(image: UIImage(systemName: "map"), style: .plain, target: self, action: #selector(showMap))
} else {
return UIBarButtonItem(title: String.localized("tab_map"), style: .plain, target: self, action: #selector(showMap))
}
}()

init(dcContext: DcContext, chatId: Int = 0) {
self.dcContext = dcContext
self.chatId = chatId
Expand Down Expand Up @@ -78,6 +86,11 @@ class AllMediaViewController: UIPageViewController {
}
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationItem.rightBarButtonItem = chatId == 0 && UserDefaults.standard.bool(forKey: "location_streaming") ? mapButton : nil
}

// MARK: - actions
@objc private func segmentControlChanged(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex < pages.count {
Expand All @@ -88,6 +101,10 @@ class AllMediaViewController: UIPageViewController {
}
}

@objc private func showMap() {
navigationController?.pushViewController(MapViewController(dcContext: dcContext, chatId: chatId), animated: true)
}

// MARK: - factory
private func makeViewController(_ page: Page) -> UIViewController {
if page.type1 == DC_MSG_IMAGE {
Expand Down
22 changes: 22 additions & 0 deletions deltachat-ios/Controller/ContactDetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@ class ContactDetailViewController: UITableViewController {
return cell
}()

private lazy var locationsCell: UITableViewCell = {
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
cell.textLabel?.text = String.localized("locations")
if #available(iOS 13.0, *) {
cell.imageView?.image = UIImage(systemName: "map") // added in ios13
}
cell.accessoryType = .disclosureIndicator
if viewModel.chatId == 0 {
cell.isUserInteractionEnabled = false
cell.textLabel?.isEnabled = false
}
return cell
}()

private lazy var statusCell: MultilineLabelCell = {
let cell = MultilineLabelCell()
cell.multilineDelegate = self
Expand Down Expand Up @@ -179,6 +193,8 @@ class ContactDetailViewController: UITableViewController {
return verifiedByCell
case .allMedia:
return allMediaCell
case .locations:
return locationsCell
case .ephemeralMessages:
return ephemeralMessagesCell
case .startChat:
Expand Down Expand Up @@ -368,6 +384,8 @@ class ContactDetailViewController: UITableViewController {
}
case .allMedia:
showAllMedia()
case .locations:
showLocations()
case .ephemeralMessages:
showEphemeralMessagesController()
case .startChat:
Expand Down Expand Up @@ -523,6 +541,10 @@ class ContactDetailViewController: UITableViewController {
navigationController?.pushViewController(AllMediaViewController(dcContext: viewModel.context, chatId: viewModel.chatId), animated: true)
}

private func showLocations() {
navigationController?.pushViewController(MapViewController(dcContext: viewModel.context, chatId: viewModel.chatId), animated: true)
}

private func showSearch() {
if let chatViewController = navigationController?.viewControllers.last(where: {
$0 is ChatViewController
Expand Down
29 changes: 25 additions & 4 deletions deltachat-ios/Controller/GroupChatDetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class GroupChatDetailViewController: UIViewController {

enum ChatOption {
case allMedia
case locations
case ephemeralMessages
}

Expand Down Expand Up @@ -142,6 +143,16 @@ class GroupChatDetailViewController: UIViewController {
return cell
}()

private lazy var locationsCell: UITableViewCell = {
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
cell.textLabel?.text = String.localized("locations")
if #available(iOS 13.0, *) {
cell.imageView?.image = UIImage(systemName: "map") // added in ios13
}
cell.accessoryType = .disclosureIndicator
return cell
}()

init(chatId: Int, dcContext: DcContext) {
self.dcContext = dcContext
self.chatId = chatId
Expand Down Expand Up @@ -279,23 +290,25 @@ class GroupChatDetailViewController: UIViewController {
private func updateOptions() {
self.editBarButtonItem.isEnabled = chat.isMailinglist || chat.canSend

self.chatOptions = [.allMedia]
if UserDefaults.standard.bool(forKey: "location_streaming") {
self.chatOptions.append(.locations)
}

if chat.isMailinglist {
self.chatOptions = [.allMedia]
self.memberManagementRows = 0
self.chatActions = [.archiveChat, .copyToClipboard, .clearChat, .deleteChat]
self.groupHeader.showMuteButton(show: true)
} else if chat.isBroadcast {
self.chatOptions = [.allMedia]
self.memberManagementRows = 1
self.chatActions = [.archiveChat, .cloneChat, .clearChat, .deleteChat]
self.groupHeader.showMuteButton(show: false)
} else if chat.canSend {
self.chatOptions = [.allMedia, .ephemeralMessages]
self.chatOptions.append(.ephemeralMessages)
self.memberManagementRows = 2
self.chatActions = [.archiveChat, .cloneChat, .leaveGroup, .clearChat, .deleteChat]
self.groupHeader.showMuteButton(show: true)
} else {
self.chatOptions = [.allMedia]
self.memberManagementRows = 0
self.chatActions = [.archiveChat, .clearChat, .deleteChat]
self.groupHeader.showMuteButton(show: true)
Expand Down Expand Up @@ -416,6 +429,10 @@ class GroupChatDetailViewController: UIViewController {
navigationController?.pushViewController(AllMediaViewController(dcContext: dcContext, chatId: chatId), animated: true)
}

private func showLocations() {
navigationController?.pushViewController(MapViewController(dcContext: dcContext, chatId: chatId), animated: true)
}

private func showSearch() {
if let chatViewController = navigationController?.viewControllers.last(where: {
$0 is ChatViewController
Expand Down Expand Up @@ -478,6 +495,8 @@ extension GroupChatDetailViewController: UITableViewDelegate, UITableViewDataSou
switch chatOptions[row] {
case .allMedia:
return allMediaCell
case .locations:
return locationsCell
case .ephemeralMessages:
return ephemeralMessagesCell
}
Expand Down Expand Up @@ -538,6 +557,8 @@ extension GroupChatDetailViewController: UITableViewDelegate, UITableViewDataSou
switch chatOptions[row] {
case .allMedia:
showAllMedia()
case .locations:
showLocations()
case .ephemeralMessages:
showEphemeralMessagesController()
}
Expand Down
87 changes: 87 additions & 0 deletions deltachat-ios/Controller/MapViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import UIKit
import WebKit
import DcCore

class MapViewController: WebxdcViewController {
private let chatId: Int
private var locationChangedObserver: NSObjectProtocol?
private var lastLocationId: Int = 0

init(dcContext: DcContext, chatId: Int) {
self.chatId = chatId
let msgIdConfigKey = "maps_webxdc_msg_id16."
var msgId = UserDefaults.standard.integer(forKey: msgIdConfigKey + String(dcContext.id))
if !dcContext.msgExists(id: msgId) {
if let path = Bundle.main.url(forResource: "maps", withExtension: "xdc", subdirectory: "Assets") {
let chatId = dcContext.createChatByContactId(contactId: Int(DC_CONTACT_ID_SELF))
let msg = dcContext.newMessage(viewType: DC_MSG_WEBXDC)
msg.setFile(filepath: path.path)
msgId = dcContext.sendMessage(chatId: chatId, message: msg) // TODO: this should be hidden by core somehow
UserDefaults.standard.setValue(msgId, forKey: msgIdConfigKey + String(dcContext.id))
}
}
super.init(dcContext: dcContext, messageId: msgId)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = nil
}

override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
if parent == nil {
removeObservers()
} else {
addObservers()
}
}

override func refreshWebxdcInfo() {
super.refreshWebxdcInfo()
title = String.localized(chatId == 0 ? "menu_show_global_map" : "locations")
}

// MARK: - setup

private func addObservers() {
locationChangedObserver = NotificationCenter.default.addObserver(forName: eventLocationChanged, object: nil, queue: nil) { [weak self]_ in
self?.updateWebxdc()
}
}

private func removeObservers() {
if let locationChangedObserver = self.locationChangedObserver {
NotificationCenter.default.removeObserver(locationChangedObserver)
}
}


// MARK: - handle updates

override func sendWebxdcStatusUpdate(payload: String, description: String) -> Bool {
guard let data: Data = payload.data(using: .utf8),
let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: AnyObject],
let payload = dict["payload"] as? [String: AnyObject] else {
return false
}

let msg = dcContext.newMessage(viewType: DC_MSG_TEXT)
msg.text = payload["label"] as? String ?? "ErrLabel"
msg.setLocation(lat: payload["lat"] as? Double ?? 0.0, lng: payload["lng"] as? Double ?? 0.0)
return dcContext.sendMessage(chatId: chatId == 0 ? dcContext.createChatByContactId(contactId: Int(DC_CONTACT_ID_SELF)) : chatId, message: msg) != 0
}

override func getWebxdcStatusUpdates(lastKnownSerial: Int) -> String {
let end = Int64(Date().timeIntervalSince1970)
let begin = end - 24*60*60
let (json, maxLocationId) = dcContext.getLocations(chatId: chatId, timestampBegin: begin, timestampEnd: 0, lastLocationId: lastLocationId)
lastLocationId = max(maxLocationId, lastLocationId)
UIPasteboard.general.string = json // TODO: remove this line, useful for debugging to get JSON out
return json
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ internal final class AdvancedViewController: UITableViewController, ProgressAler
if !locationStreaming {
let alert = UIAlertController(title: "Thanks for trying out the experimental feature 🧪 \"Location streaming\"",
message: "You will find a corresponding option in the attach menu (the paper clip) of each chat now.\n\n"
+ "Moreover, \"Profiles\" and \"All Media\" will offer a map.\n\n"
+ "If you want to quit the experimental feature, you can disable it at \"Settings / Advanced\".",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: String.localized("ok"), style: .default, handler: nil))
Expand Down
17 changes: 12 additions & 5 deletions deltachat-ios/Controller/WebxdcViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ class WebxdcViewController: WebViewViewController {
}
}

private func refreshWebxdcInfo() {
func refreshWebxdcInfo() {
let msg = dcContext.getMessage(id: messageId)
let dict = msg.getWebxdcInfoDict()

Expand Down Expand Up @@ -372,7 +372,7 @@ class WebxdcViewController: WebViewViewController {
}
}

private func updateWebxdc() {
func updateWebxdc() {
webView.evaluateJavaScript("window.__webxdcUpdate()", completionHandler: nil)
}

Expand Down Expand Up @@ -414,6 +414,14 @@ class WebxdcViewController: WebViewViewController {
private func shareWebxdc(_ action: UIAlertAction) {
Utils.share(message: dcContext.getMessage(id: messageId), parentViewController: self, sourceItem: moreButton)
}

func sendWebxdcStatusUpdate(payload: String, description: String) -> Bool {
return dcContext.sendWebxdcStatusUpdate(msgId: messageId, payload: payload, description: description)
}

func getWebxdcStatusUpdates(lastKnownSerial: Int) -> String {
return dcContext.getWebxdcStatusUpdates(msgId: messageId, lastKnownSerial: lastKnownSerial)
}
}

extension WebxdcViewController: WKScriptMessageHandler {
Expand All @@ -436,7 +444,7 @@ extension WebxdcViewController: WKScriptMessageHandler {
logger.error("Failed to parse status update parameters \(message.body)")
return
}
_ = dcContext.sendWebxdcStatusUpdate(msgId: messageId, payload: payloadString, description: description)
_ = sendWebxdcStatusUpdate(payload: payloadString, description: description)

case .sendToChat:
if let dict = message.body as? [String: AnyObject] {
Expand Down Expand Up @@ -477,8 +485,7 @@ extension WebxdcViewController: WKURLSchemeHandler {
let statusCode: Int
if url.path == "/webxdc-update.json" || url.path == "webxdc-update.json" {
let lastKnownSerial = Int(url.query ?? "0") ?? 0
data = Data(
dcContext.getWebxdcStatusUpdates(msgId: messageId, lastKnownSerial: lastKnownSerial).utf8)
data = Data(getWebxdcStatusUpdates(lastKnownSerial: lastKnownSerial).utf8)
mimeType = "application/json; charset=utf-8"
statusCode = 200
} else {
Expand Down
Loading

0 comments on commit 96c50d9

Please sign in to comment.