|  |  | @ -2,7 +2,6 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #include <QApplication> |  |  |  | #include <QApplication> | 
			
		
	
		
		
			
				
					
					|  |  |  | #include <QDebug> |  |  |  | #include <QDebug> | 
			
		
	
		
		
			
				
					
					|  |  |  | #include <vector> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #include "common/util.h" |  |  |  | #include "common/util.h" | 
			
		
	
		
		
			
				
					
					|  |  |  | #include "selfdrive/ui/qt/request_repeater.h" |  |  |  | #include "selfdrive/ui/qt/request_repeater.h" | 
			
		
	
	
		
		
			
				
					|  |  | @ -12,11 +11,8 @@ static QString shorten(const QString &str, int max_len) { | 
			
		
	
		
		
			
				
					
					|  |  |  |   return str.size() > max_len ? str.left(max_len).trimmed() + "…" : str; |  |  |  |   return str.size() > max_len ? str.left(max_len).trimmed() + "…" : str; | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | MapSettings::MapSettings(bool closeable, QWidget *parent) |  |  |  | MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     : QFrame(parent), current_destination(nullptr) { |  |  |  |   close_icon = loadPixmap("../assets/icons/close.svg", {100, 100}); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |   QSize icon_size(100, 100); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   close_icon = loadPixmap("../assets/icons/close.svg", icon_size); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |   setContentsMargins(0, 0, 0, 0); |  |  |  |   setContentsMargins(0, 0, 0, 0); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   auto *frame = new QVBoxLayout(this); |  |  |  |   auto *frame = new QVBoxLayout(this); | 
			
		
	
	
		
		
			
				
					|  |  | @ -68,7 +64,7 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   current_widget = new DestinationWidget(this); |  |  |  |   current_widget = new DestinationWidget(this); | 
			
		
	
		
		
			
				
					
					|  |  |  |   QObject::connect(current_widget, &DestinationWidget::actionClicked, [=]() { |  |  |  |   QObject::connect(current_widget, &DestinationWidget::actionClicked, [=]() { | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (!current_destination) return; |  |  |  |     if (current_destination.empty()) return; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     params.remove("NavDestination"); |  |  |  |     params.remove("NavDestination"); | 
			
		
	
		
		
			
				
					
					|  |  |  |     updateCurrentRoute(); |  |  |  |     updateCurrentRoute(); | 
			
		
	
		
		
			
				
					
					|  |  |  |   }); |  |  |  |   }); | 
			
		
	
	
		
		
			
				
					|  |  | @ -85,7 +81,7 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   setStyleSheet("MapSettings { background-color: #333333; }"); |  |  |  |   setStyleSheet("MapSettings { background-color: #333333; }"); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   QObject::connect(NavigationRequest::instance(), &NavigationRequest::locationsUpdated, this, &MapSettings::parseResponse); |  |  |  |   QObject::connect(NavigationRequest::instance(), &NavigationRequest::locationsUpdated, this, &MapSettings::updateLocations); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |   QObject::connect(NavigationRequest::instance(), &NavigationRequest::nextDestinationUpdated, this, &MapSettings::updateCurrentRoute); |  |  |  |   QObject::connect(NavigationRequest::instance(), &NavigationRequest::nextDestinationUpdated, this, &MapSettings::updateCurrentRoute); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -106,72 +102,37 @@ void MapSettings::updateCurrentRoute() { | 
			
		
	
		
		
			
				
					
					|  |  |  |       qWarning() << "JSON Parse failed on NavDestination" << dest; |  |  |  |       qWarning() << "JSON Parse failed on NavDestination" << dest; | 
			
		
	
		
		
			
				
					
					|  |  |  |       return; |  |  |  |       return; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |     auto destination = std::make_unique<NavDestination>(doc.object()); |  |  |  |     current_destination = doc.object(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     if (current_destination && *destination == *current_destination) return; |  |  |  |     current_widget->set(current_destination, true); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     current_destination = std::move(destination); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     current_widget->set(current_destination.get(), true); |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |   } else { |  |  |  |   } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |     current_destination.reset(nullptr); |  |  |  |     current_destination = {}; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     current_widget->unset("", true); |  |  |  |     current_widget->unset("", true); | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  |   if (isVisible()) refresh(); |  |  |  |   if (isVisible()) refresh(); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | void MapSettings::parseResponse(const QString &response, bool success) { |  |  |  | void MapSettings::updateLocations(const QJsonArray &locations) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |   if (!success || response == cur_destinations) return; |  |  |  |   current_locations = locations; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |   cur_destinations = response; |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |   refresh(); |  |  |  |   refresh(); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | void MapSettings::refresh() { |  |  |  | void MapSettings::refresh() { | 
			
		
	
		
		
			
				
					
					|  |  |  |   bool has_home = false, has_work = false; |  |  |  |   setUpdatesEnabled(false); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |   auto destinations = std::vector<std::unique_ptr<NavDestination>>(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   auto destinations_str = cur_destinations.trimmed(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   if (!destinations_str.isEmpty()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     QJsonDocument doc = QJsonDocument::fromJson(destinations_str.toUtf8()); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (doc.isNull()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       qWarning() << "JSON Parse failed on navigation locations" << cur_destinations; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       return; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     for (auto el : doc.array()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       auto destination = std::make_unique<NavDestination>(el.toObject()); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       // add home and work later if they are missing
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       if (destination->isFavorite()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (destination->label() == NAV_FAVORITE_LABEL_HOME) has_home = true; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         else if (destination->label() == NAV_FAVORITE_LABEL_WORK) has_work = true; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       // skip current destination
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       if (current_destination && *destination == *current_destination) continue; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       destinations.push_back(std::move(destination)); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |   // TODO: should we build a new layout and swap it in?
 |  |  |  |   // TODO: should we build a new layout and swap it in?
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   clearLayout(destinations_layout); |  |  |  |   clearLayout(destinations_layout); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   // Sort: HOME, WORK, alphabetical FAVORITES, and then most recent (as returned by API)
 |  |  |  |   bool has_home = false, has_work = false; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |   std::stable_sort(destinations.begin(), destinations.end(), [](const auto &a, const auto &b) { |  |  |  |   for (auto location : current_locations) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     if (a->isFavorite() && b->isFavorite()) { |  |  |  |     auto dest = location.toObject(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       if (a->label() == NAV_FAVORITE_LABEL_HOME) return true; |  |  |  |     if (dest["save_type"].toString() == NAV_TYPE_FAVORITE) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       else if (b->label() == NAV_FAVORITE_LABEL_HOME) return false; |  |  |  |       has_home = has_home || dest["label"].toString() == NAV_FAVORITE_LABEL_HOME; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       else if (a->label() == NAV_FAVORITE_LABEL_WORK) return true; |  |  |  |       has_work = has_work || dest["label"].toString() == NAV_FAVORITE_LABEL_WORK; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       else if (b->label() == NAV_FAVORITE_LABEL_WORK) return false; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       else return a->name() < b->name(); |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |     else if (a->isFavorite()) return true; |  |  |  |     if (dest == current_destination) continue; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     else if (b->isFavorite()) return false; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return false; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   }); |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   for (auto &destination : destinations) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     auto widget = new DestinationWidget(this); |  |  |  |     auto widget = new DestinationWidget(this); | 
			
		
	
		
		
			
				
					
					|  |  |  |     widget->set(destination.get(), false); |  |  |  |     widget->set(dest, false); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |     QObject::connect(widget, &QPushButton::clicked, [this, dest]() { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     QObject::connect(widget, &QPushButton::clicked, [this, dest = destination->toJson()]() { |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |       navigateTo(dest); |  |  |  |       navigateTo(dest); | 
			
		
	
		
		
			
				
					
					|  |  |  |       emit closeSettings(); |  |  |  |       emit closeSettings(); | 
			
		
	
		
		
			
				
					
					|  |  |  |     }); |  |  |  |     }); | 
			
		
	
	
		
		
			
				
					|  |  | @ -189,11 +150,12 @@ void MapSettings::refresh() { | 
			
		
	
		
		
			
				
					
					|  |  |  |     auto widget = new DestinationWidget(this); |  |  |  |     auto widget = new DestinationWidget(this); | 
			
		
	
		
		
			
				
					
					|  |  |  |     widget->unset(NAV_FAVORITE_LABEL_WORK); |  |  |  |     widget->unset(NAV_FAVORITE_LABEL_WORK); | 
			
		
	
		
		
			
				
					
					|  |  |  |     // TODO: refactor to remove this hack
 |  |  |  |     // TODO: refactor to remove this hack
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     int index = !has_home || (current_destination && current_destination->isFavorite() && current_destination->label() == NAV_FAVORITE_LABEL_HOME) ? 0 : 1; |  |  |  |     int index = !has_home || (current_destination["save_type"] == NAV_TYPE_FAVORITE && current_destination["label"] == NAV_FAVORITE_LABEL_HOME) ? 0 : 1; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     destinations_layout->insertWidget(index, widget); |  |  |  |     destinations_layout->insertWidget(index, widget); | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   destinations_layout->addStretch(); |  |  |  |   destinations_layout->addStretch(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   setUpdatesEnabled(true); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | void MapSettings::navigateTo(const QJsonObject &place) { |  |  |  | void MapSettings::navigateTo(const QJsonObject &place) { | 
			
		
	
	
		
		
			
				
					|  |  | @ -263,23 +225,23 @@ DestinationWidget::DestinationWidget(QWidget *parent) : QPushButton(parent) { | 
			
		
	
		
		
			
				
					
					|  |  |  |   )"); |  |  |  |   )"); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | void DestinationWidget::set(NavDestination *destination, bool current) { |  |  |  | void DestinationWidget::set(const QJsonObject &destination, bool current) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |   setProperty("current", current); |  |  |  |   setProperty("current", current); | 
			
		
	
		
		
			
				
					
					|  |  |  |   setProperty("set", true); |  |  |  |   setProperty("set", true); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   auto icon_pixmap = current ? icons().directions : icons().recent; |  |  |  |   auto icon_pixmap = current ? icons().directions : icons().recent; | 
			
		
	
		
		
			
				
					
					|  |  |  |   auto title_text = destination->name(); |  |  |  |   auto title_text = destination["place_name"].toString(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |   auto subtitle_text = destination->details(); |  |  |  |   auto subtitle_text = destination["place_details"].toString(); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   if (destination->isFavorite()) { |  |  |  |   if (destination["save_type"] == NAV_TYPE_FAVORITE) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     if (destination->label() == NAV_FAVORITE_LABEL_HOME) { |  |  |  |     if (destination["label"] == NAV_FAVORITE_LABEL_HOME) { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |       icon_pixmap = icons().home; |  |  |  |       icon_pixmap = icons().home; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       subtitle_text = title_text + ", " + subtitle_text; | 
			
		
	
		
		
			
				
					
					|  |  |  |       title_text = tr("Home"); |  |  |  |       title_text = tr("Home"); | 
			
		
	
		
		
			
				
					
					|  |  |  |       subtitle_text = destination->name() + ", " + destination->details(); |  |  |  |     } else if (destination["label"] == NAV_FAVORITE_LABEL_WORK) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     } else if (destination->label() == NAV_FAVORITE_LABEL_WORK) { |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |       icon_pixmap = icons().work; |  |  |  |       icon_pixmap = icons().work; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       subtitle_text = title_text + ", " + subtitle_text; | 
			
		
	
		
		
			
				
					
					|  |  |  |       title_text = tr("Work"); |  |  |  |       title_text = tr("Work"); | 
			
		
	
		
		
			
				
					
					|  |  |  |       subtitle_text = destination->name() + ", " + destination->details(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } else { |  |  |  |     } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |       icon_pixmap = icons().favorite; |  |  |  |       icon_pixmap = icons().favorite; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
	
		
		
			
				
					|  |  | @ -332,9 +294,8 @@ NavigationRequest::NavigationRequest(QObject *parent) : QObject(parent) { | 
			
		
	
		
		
			
				
					
					|  |  |  |       // Fetch favorite and recent locations
 |  |  |  |       // Fetch favorite and recent locations
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/locations"; |  |  |  |       QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/locations"; | 
			
		
	
		
		
			
				
					
					|  |  |  |       RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_NavDestinations", 30, true); |  |  |  |       RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_NavDestinations", 30, true); | 
			
		
	
		
		
			
				
					
					|  |  |  |       QObject::connect(repeater, &RequestRepeater::requestDone, this, &NavigationRequest::locationsUpdated); |  |  |  |       QObject::connect(repeater, &RequestRepeater::requestDone, this, &NavigationRequest::parseLocationsResponse); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     { |  |  |  |     { | 
			
		
	
		
		
			
				
					
					|  |  |  |       // Destination set while offline
 |  |  |  |       // Destination set while offline
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/next"; |  |  |  |       QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/next"; | 
			
		
	
	
		
		
			
				
					|  |  | @ -358,3 +319,29 @@ NavigationRequest::NavigationRequest(QObject *parent) : QObject(parent) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | static void swap(QJsonValueRef v1, QJsonValueRef v2) { std::swap(v1, v2); } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | void NavigationRequest::parseLocationsResponse(const QString &response, bool success) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   if (!success || response == prev_response) return; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   prev_response = response; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   QJsonDocument doc = QJsonDocument::fromJson(response.trimmed().toUtf8()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   if (doc.isNull()) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     qWarning() << "JSON Parse failed on navigation locations" << response; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     return; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   // Sort: HOME, WORK, alphabetical FAVORITES, and then most recent (as returned by API)
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   QJsonArray locations = doc.array(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   std::stable_sort(locations.begin(), locations.end(), [](const QJsonValue &a, const QJsonValue &b) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     if (a["save_type"] == NAV_TYPE_FAVORITE || b["save_type"] == NAV_TYPE_FAVORITE) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       QString a_label = a["label"].toString(), b_label = b["label"].toString(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       return std::tuple(a["save_type"].toString(), (a_label.isEmpty() ? "xxx" : a_label), a["place_name"].toString()) < | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |              std::tuple(b["save_type"].toString(), (b_label.isEmpty() ? "xxx" : b_label), b["place_name"].toString()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       return false; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   }); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   emit locationsUpdated(locations); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | 
 |