[notifications] ugc rate notification business logic

This commit is contained in:
Arsentiy Milchakov 2018-11-08 19:26:03 +03:00 committed by Tatiana Yan
parent 052e75adda
commit 67d6885283
10 changed files with 524 additions and 62 deletions

View file

@ -395,6 +395,7 @@ Framework::Framework(FrameworkParams const & params)
, m_popularityLoader(m_model.GetDataSource())
, m_purchase(std::make_unique<Purchase>())
, m_tipsApi(static_cast<TipsApi::Delegate &>(*this))
, m_notificationManager(static_cast<notifications::NotificationManager::Delegate &>(*this))
{
CHECK(IsLittleEndian(), ("Only little-endian architectures are supported."));
@ -511,6 +512,8 @@ Framework::Framework(FrameworkParams const & params)
InitTransliteration();
LOG(LDEBUG, ("Transliterators initialized"));
m_notificationManager.Load();
m_notificationManager.TrimExpired();
eye::Eye::Instance().TrimExpired();
eye::Eye::Instance().Subscribe(&m_notificationManager);
}

View file

@ -137,7 +137,8 @@ struct FrameworkParams
class Framework : public SearchAPI::Delegate,
public RoutingManager::Delegate,
public TipsApi::Delegate
public TipsApi::Delegate,
public notifications::NotificationManager::Delegate
{
DISALLOW_COPY(Framework);
@ -852,7 +853,7 @@ private:
//@}
public:
storage::TCountriesVec GetTopmostCountries(ms::LatLon const & latlon) const;
storage::TCountriesVec GetTopmostCountries(ms::LatLon const & latlon) const override;
private:
unique_ptr<search::CityFinder> m_cityFinder;

View file

@ -94,8 +94,7 @@ public:
if (request & REQUEST_TYPE_NOTIFICATION)
{
m_notificationManager = std::make_unique<notifications::NotificationManager>();
m_notificationManager->Load();
m_notificationManager = std::make_unique<lightweight::NotificationManager>();
request ^= REQUEST_TYPE_NOTIFICATION;
}
@ -123,7 +122,7 @@ private:
std::unique_ptr<CountryInfoReader> m_countryInfoReader;
std::unique_ptr<LocalAdsFeaturesReader> m_localAdsFeaturesReader;
std::unique_ptr<Statistics> m_localAdsStatistics;
std::unique_ptr<notifications::NotificationManager> m_notificationManager;
std::unique_ptr<lightweight::NotificationManager> m_notificationManager;
};
template<>

View file

@ -1,5 +1,6 @@
#include "testing/testing.hpp"
#include "map/notifications/notification_manager.hpp"
#include "map/notifications/notification_queue.hpp"
#include "map/notifications/notification_queue_serdes.hpp"
#include "map/notifications/notification_queue_storage.hpp"
@ -13,6 +14,20 @@
using namespace notifications;
namespace notifications
{
class NotificationManagerForTesting : public NotificationManager
{
public:
explicit NotificationManagerForTesting(NotificationManager::Delegate const & delegate)
: NotificationManager(delegate)
{
}
Queue & GetEditableQueue() { return m_queue; }
};
} // namespace notifications
namespace
{
class ScopedNotificationsQueue
@ -24,13 +39,28 @@ public:
}
};
class DelegateForTesting : public NotificationManager::Delegate
{
public:
// NotificationManager::Delegate overrides:
storage::TCountriesVec GetTopmostCountries(ms::LatLon const & latlon) const override
{
return m_countries;
}
void SetCountries(storage::TCountriesVec const & countries) { m_countries = countries; }
private:
storage::TCountriesVec m_countries;
};
Queue MakeDefaultQueueForTesting()
{
Queue queue;
{
Notification notification;
notification.m_type = Notification::Type::UgcReview;
NotificationCandidate notification;
notification.m_type = NotificationCandidate::Type::UgcReview;
notification.m_mapObject = std::make_unique<eye::MapObject>();
notification.m_mapObject->SetBestType("cafe");
@ -41,8 +71,8 @@ Queue MakeDefaultQueueForTesting()
}
{
Notification notification;
notification.m_type = Notification::Type::UgcReview;
NotificationCandidate notification;
notification.m_type = NotificationCandidate::Type::UgcReview;
notification.m_mapObject = std::make_unique<eye::MapObject>();
notification.m_mapObject->SetBestType("shop");
@ -53,8 +83,8 @@ Queue MakeDefaultQueueForTesting()
}
{
Notification notification;
notification.m_type = Notification::Type::UgcReview;
NotificationCandidate notification;
notification.m_type = NotificationCandidate::Type::UgcReview;
notification.m_mapObject = std::make_unique<eye::MapObject>();
notification.m_mapObject->SetBestType("viewpoint");
@ -111,4 +141,170 @@ UNIT_CLASS_TEST(ScopedNotificationsQueue, Notifications_QueueSaveLoadTest)
CompareWithDefaultQueue(result);
}
UNIT_CLASS_TEST(ScopedNotificationsQueue, Notifications_UgcRateCheckRouteToInSameGeoTrigger)
{
DelegateForTesting delegate;
NotificationManagerForTesting notificationManager(delegate);
delegate.SetCountries({"Norway_Central"});
eye::MapObject mapObject;
mapObject.SetPos(MercatorBounds::FromLatLon({59.909299, 10.769807}));
mapObject.SetReadableName("Visiting a Bjarne");
mapObject.SetBestType("amenity-bar");
mapObject.SetMwmNames({"Norway_Central"});
eye::MapObject::Event event;
event.m_type = eye::MapObject::Event::Type::RouteToCreated;
event.m_userPos = MercatorBounds::FromLatLon({59.920333, 10.780793});
event.m_eventTime = notifications::Clock::now() - std::chrono::hours(25);
mapObject.GetEditableEvents().push_back(event);
notificationManager.OnMapObjectEvent(mapObject);
TEST_EQUAL(notificationManager.GetEditableQueue().m_candidates.size(), 1, ());
notificationManager.GetEditableQueue().m_candidates[0].m_timeOfLastEvent = event.m_eventTime;
auto result = notificationManager.GetNotification();
TEST(result.is_initialized(), ());
TEST_EQUAL(result.get().m_type, NotificationCandidate::Type::UgcReview, ());
result = notificationManager.GetNotification();
TEST(!result.is_initialized(), ());
}
UNIT_CLASS_TEST(ScopedNotificationsQueue, Notifications_UgcRateCheckUgcNotSavedTrigger)
{
DelegateForTesting delegate;
NotificationManagerForTesting notificationManager(delegate);
delegate.SetCountries({"Norway_Central"});
eye::MapObject mapObject;
mapObject.SetPos(MercatorBounds::FromLatLon({59.909299, 10.769807}));
mapObject.SetReadableName("Visiting a Bjarne");
mapObject.SetBestType("amenity-bar");
mapObject.SetMwmNames({"Norway_Central"});
{
eye::MapObject::Event event;
event.m_type = eye::MapObject::Event::Type::Open;
event.m_userPos = MercatorBounds::FromLatLon({59.920333, 10.780793});
event.m_eventTime = notifications::Clock::now() - std::chrono::hours(25);
mapObject.GetEditableEvents().push_back(event);
notificationManager.OnMapObjectEvent(mapObject);
}
TEST_EQUAL(notificationManager.GetEditableQueue().m_candidates.size(), 0, ());
{
eye::MapObject::Event event;
event.m_type = eye::MapObject::Event::Type::UgcEditorOpened;
event.m_userPos = MercatorBounds::FromLatLon({59.920333, 10.780793});
event.m_eventTime = notifications::Clock::now() - std::chrono::hours(25);
mapObject.GetEditableEvents().push_back(event);
notificationManager.OnMapObjectEvent(mapObject);
}
TEST_EQUAL(notificationManager.GetEditableQueue().m_candidates.size(), 1, ());
auto result = notificationManager.GetNotification();
TEST(!result.is_initialized(), ());
notificationManager.GetEditableQueue().m_candidates[0].m_timeOfLastEvent =
notifications::Clock::now() - std::chrono::hours(25);
result = notificationManager.GetNotification();
TEST(result.is_initialized(), ());
TEST_EQUAL(result.get().m_type, NotificationCandidate::Type::UgcReview, ());
result = notificationManager.GetNotification();
TEST(!result.is_initialized(), ());
{
eye::MapObject::Event event;
event.m_type = eye::MapObject::Event::Type::UgcEditorOpened;
event.m_userPos = MercatorBounds::FromLatLon({59.920333, 10.780793});
event.m_eventTime = notifications::Clock::now() - std::chrono::hours(25);
mapObject.GetEditableEvents().push_back(event);
notificationManager.OnMapObjectEvent(mapObject);
}
{
eye::MapObject::Event event;
event.m_type = eye::MapObject::Event::Type::UgcSaved;
event.m_userPos = MercatorBounds::FromLatLon({59.920333, 10.780793});
event.m_eventTime = notifications::Clock::now() - std::chrono::hours(25);
mapObject.GetEditableEvents().push_back(event);
notificationManager.OnMapObjectEvent(mapObject);
}
result = notificationManager.GetNotification();
TEST(!result.is_initialized(), ());
}
UNIT_CLASS_TEST(ScopedNotificationsQueue, Notifications_UgcRateCheckPlannedTripTrigger)
{
DelegateForTesting delegate;
NotificationManagerForTesting notificationManager(delegate);
delegate.SetCountries({"Norway_Central"});
eye::MapObject mapObject;
mapObject.SetPos(MercatorBounds::FromLatLon({59.909299, 10.769807}));
mapObject.SetReadableName("Visiting a Bjarne");
mapObject.SetBestType("amenity-bar");
mapObject.SetMwmNames({"Norway_Central"});
{
eye::MapObject::Event event;
event.m_type = eye::MapObject::Event::Type::Open;
event.m_userPos = MercatorBounds::FromLatLon({54.637300, 19.877731});
event.m_eventTime = notifications::Clock::now() - std::chrono::hours(25);
mapObject.GetEditableEvents().push_back(event);
notificationManager.OnMapObjectEvent(mapObject);
}
TEST_EQUAL(notificationManager.GetEditableQueue().m_candidates.size(), 0, ());
{
eye::MapObject::Event event;
event.m_type = eye::MapObject::Event::Type::Open;
event.m_userPos = MercatorBounds::FromLatLon({54.637310, 19.877735});
event.m_eventTime = notifications::Clock::now() - std::chrono::hours(25);
mapObject.GetEditableEvents().push_back(event);
notificationManager.OnMapObjectEvent(mapObject);
}
TEST_EQUAL(notificationManager.GetEditableQueue().m_candidates.size(), 0, ());
{
eye::MapObject::Event event;
event.m_type = eye::MapObject::Event::Type::RouteToCreated;
event.m_userPos = MercatorBounds::FromLatLon({59.920333, 10.780793});
event.m_eventTime = notifications::Clock::now() - std::chrono::hours(25);
mapObject.GetEditableEvents().push_back(event);
notificationManager.OnMapObjectEvent(mapObject);
}
TEST_EQUAL(notificationManager.GetEditableQueue().m_candidates.size(), 1, ());
auto result = notificationManager.GetNotification();
TEST(!result.is_initialized(), ());
notificationManager.GetEditableQueue().m_candidates[0].m_timeOfLastEvent =
notifications::Clock::now() - std::chrono::hours(25);
result = notificationManager.GetNotification();
TEST(result.is_initialized(), ());
TEST_EQUAL(result.get().m_type, NotificationCandidate::Type::UgcReview, ());
result = notificationManager.GetNotification();
TEST(!result.is_initialized(), ());
}
} // namespace

View file

@ -4,9 +4,122 @@
#include "map/notifications/notification_queue_storage.hpp"
#include "base/logging.hpp"
#include "base/macros.hpp"
#include <algorithm>
#include <array>
#include <utility>
using namespace notifications;
namespace
{
auto constexpr kCandidatesExpirePeriod = std::chrono::hours(24 * 30);
auto constexpr kPeriodBetweenNotifications = std::chrono::hours(24 * 7);
auto constexpr kMinTimeSinceLastEventForUgcRate = std::chrono::hours(24);
std::array<std::string, 4> const kUgcRateSupportedTypes = {"amenity-bar", "amenity-cafe",
"amenity-pub", "amenity-restaurant"};
double constexpr kMinDistanceToTriggerUgcRateInMeters = 50000.0; // 50 km
uint32_t constexpr kOpenCountForPlannedTripTrigger = 2;
bool CheckUgcNotSavedTrigger(eye::MapObject const & poi)
{
bool ugcEditorShowed = false;
bool ugcSaved = false;
for (auto const & event : poi.GetEvents())
{
if (event.m_type == eye::MapObject::Event::Type::UgcEditorOpened && !ugcEditorShowed)
{
ugcEditorShowed = true;
}
else if (event.m_type == eye::MapObject::Event::Type::UgcSaved)
{
ugcSaved = true;
break;
}
}
if (ugcEditorShowed && !ugcSaved)
return true;
return false;
}
bool IsSmallDistanceAndSameMwm(eye::MapObject const & poi, eye::MapObject::Event const & event,
storage::TCountriesVec const & userMwms)
{
auto const distanceToUser = MercatorBounds::DistanceOnEarth(event.m_userPos, poi.GetPos());
if (distanceToUser > kMinDistanceToTriggerUgcRateInMeters)
return false;
auto const & poiMwms = poi.GetMwmNames();
return std::any_of(userMwms.cbegin(), userMwms.cend(), [&poiMwms](auto const & userMwm)
{
return std::find(poiMwms.cbegin(), poiMwms.cend(), userMwm) != poiMwms.cend();
});
}
bool CheckRouteToInSameGeoTrigger(eye::MapObject const & poi,
storage::TCountriesVec const & userMwms)
{
CHECK_GREATER(poi.GetEvents().size(), 0, ());
auto const & lastEvent = poi.GetEvents().back();
if (lastEvent.m_type != eye::MapObject::Event::Type::RouteToCreated)
return false;
return IsSmallDistanceAndSameMwm(poi, lastEvent, userMwms);
}
bool CheckPlannedTripTrigger(eye::MapObject const & poi, storage::TCountriesVec const & userMwms)
{
CHECK_GREATER(poi.GetEvents().size(), 0, ());
auto const & events = poi.GetEvents();
if (events.back().m_type != eye::MapObject::Event::Type::Open)
return false;
if (!IsSmallDistanceAndSameMwm(poi, events.back(), userMwms))
return false;
uint32_t openCounter = 0;
for (size_t i = 0; i < events.size() - 1; ++i)
{
if (events[i].m_type != eye::MapObject::Event::Type::Open &&
events[i].m_type != eye::MapObject::Event::Type::AddToBookmark &&
events[i].m_type != eye::MapObject::Event::Type::RouteToCreated)
{
continue;
}
if (IsSmallDistanceAndSameMwm(poi, events[i], userMwms))
continue;
if (events[i].m_type == eye::MapObject::Event::Type::Open)
++openCounter;
else
return true;
return openCounter >= kOpenCountForPlannedTripTrigger;
}
return false;
}
} // namespace
namespace notifications
{
NotificationManager::NotificationManager(NotificationManager::Delegate const & delegate)
: m_delegate(delegate)
{
}
void NotificationManager::Load()
{
std::vector<int8_t> queueFileData;
@ -27,19 +140,47 @@ void NotificationManager::Load()
}
}
boost::optional<Notification> NotificationManager::GetNotification() const
void NotificationManager::TrimExpired()
{
if (m_queue.m_candidates.empty())
auto & candidates = m_queue.m_candidates;
candidates.erase(std::remove_if(candidates.begin(), candidates.end(), [](auto const & item)
{
if (item.m_used.time_since_epoch().count() != 0)
return Clock::now() - item.m_used >= eye::Eye::GetMapObjectEventsExpirePeriod();
return Clock::now() - item.m_created >= kCandidatesExpirePeriod;
}), candidates.end());
VERIFY(Save(), ());
}
boost::optional<NotificationCandidate> NotificationManager::GetNotification()
{
if (Clock::now() - m_queue.m_lastNotificationProvidedTime < kPeriodBetweenNotifications)
return {};
// Is not implemented yet. Coming soon.
auto & candidates = m_queue.m_candidates;
return {};
if (candidates.empty())
return {};
auto it = GetUgcRateCandidate();
if (it == candidates.end())
return {};
it->m_used = Clock::now();
m_queue.m_lastNotificationProvidedTime = Clock::now();
VERIFY(Save(), ());
return *it;
}
void NotificationManager::OnMapObjectEvent(eye::MapObject const & poi)
{
// Is not implemented yet. Coming soon.
ProcessUgcRateCandidates(poi);
}
bool NotificationManager::Save()
@ -48,4 +189,76 @@ bool NotificationManager::Save()
QueueSerdes::Serialize(m_queue, fileData);
return QueueStorage::Save(fileData);
}
void NotificationManager::ProcessUgcRateCandidates(eye::MapObject const & poi)
{
if (poi.GetReadableName().empty())
return;
{
auto const it = std::find(kUgcRateSupportedTypes.cbegin(), kUgcRateSupportedTypes.cend(),
poi.GetBestType());
if (it == kUgcRateSupportedTypes.cend())
return;
}
auto it = m_queue.m_candidates.begin();
for (; it != m_queue.m_candidates.end(); ++it)
{
if (it->m_type != NotificationCandidate::Type::UgcReview || !it->m_mapObject ||
it->m_used.time_since_epoch().count() != 0)
{
continue;
}
if (it->m_mapObject->AlmostEquals(poi))
{
if (poi.GetEvents().back().m_type == eye::MapObject::Event::Type::UgcSaved &&
CheckUgcNotSavedTrigger(poi))
{
m_queue.m_candidates.erase(it);
VERIFY(Save(), ());
break;
}
it->m_timeOfLastEvent = Clock::now();
VERIFY(Save(), ());
return;
}
}
CHECK_GREATER(poi.GetEvents().size(), 0, ());
auto const latLon = MercatorBounds::ToLatLon(poi.GetEvents().back().m_userPos);
auto const userMwms = m_delegate.GetTopmostCountries(std::move(latLon));
if (CheckUgcNotSavedTrigger(poi) || CheckRouteToInSameGeoTrigger(poi, userMwms) ||
CheckPlannedTripTrigger(poi, userMwms))
{
NotificationCandidate candidate;
candidate.m_type = NotificationCandidate::Type::UgcReview;
candidate.m_created = Clock::now();
candidate.m_timeOfLastEvent = candidate.m_created;
candidate.m_mapObject = std::make_shared<eye::MapObject>(poi);
m_queue.m_candidates.emplace_back(std::move(candidate));
VERIFY(Save(), ());
}
}
Candidates::iterator NotificationManager::GetUgcRateCandidate()
{
auto it = m_queue.m_candidates.begin();
for (; it != m_queue.m_candidates.end(); ++it)
{
if (it->m_used.time_since_epoch().count() == 0 &&
it->m_type == NotificationCandidate::Type::UgcReview &&
Clock::now() - it->m_timeOfLastEvent >= kMinTimeSinceLastEventForUgcRate)
{
return it;
}
}
return it;
}
} // namespace notifications

View file

@ -13,17 +13,60 @@ namespace notifications
class NotificationManager : public eye::Subscriber
{
public:
void Load();
friend class NotificationManagerForTesting;
boost::optional<Notification> GetNotification() const;
class Delegate
{
public:
virtual ~Delegate() = default;
virtual storage::TCountriesVec GetTopmostCountries(ms::LatLon const & latlon) const = 0;
};
explicit NotificationManager(Delegate const & delegate);
void Load();
void TrimExpired();
boost::optional<NotificationCandidate> GetNotification();
// eye::Subscriber overrides:
void OnMapObjectEvent(eye::MapObject const & poi) override;
private:
bool Save();
void ProcessUgcRateCandidates(eye::MapObject const & poi);
Candidates::iterator GetUgcRateCandidate();
Delegate const & m_delegate;
// Notification candidates queue.
Queue m_queue;
};
} // namespace notifications
namespace lightweight
{
class NotificationManager
{
public:
NotificationManager() : m_manager(m_delegate) { m_manager.Load(); }
boost::optional<notifications::NotificationCandidate> GetNotification()
{
return m_manager.GetNotification();
}
private:
class EmptyDelegate : public notifications::NotificationManager::Delegate
{
public:
// NotificationManager::Delegate overrides:
storage::TCountriesVec GetTopmostCountries(ms::LatLon const & latlon) const override
{
return {};
}
};
EmptyDelegate m_delegate;
notifications::NotificationManager m_manager;
};
} // namespace lightweight

View file

@ -8,7 +8,10 @@
namespace notifications
{
struct Notification
using Clock = std::chrono::system_clock;
using Time = Clock::time_point;
struct NotificationCandidate
{
enum class Type : uint8_t
{
@ -16,13 +19,17 @@ struct Notification
UgcReview
};
DECLARE_VISITOR(visitor(m_type, "type"), visitor(m_mapObject, "object"));
DECLARE_VISITOR(visitor(m_type, "type"), visitor(m_created, "created_time"),
visitor(m_used, "used"), visitor(m_mapObject, "object"));
Type m_type;
std::unique_ptr<eye::MapObject> m_mapObject;
Time m_created;
Time m_used;
Time m_timeOfLastEvent;
std::shared_ptr<eye::MapObject> m_mapObject;
};
using Candidates = std::deque<Notification>;
using Candidates = std::deque<NotificationCandidate>;
enum class Version : int8_t
{
@ -35,19 +42,20 @@ struct QueueV0
{
static Version GetVersion() { return Version::V0; }
DECLARE_VISITOR(visitor(m_candidates, "queue"))
DECLARE_VISITOR(visitor(m_candidates, "queue"));
Time m_lastNotificationProvidedTime;
Candidates m_candidates;
};
using Queue = QueueV0;
inline std::string DebugPrint(Notification::Type type)
inline std::string DebugPrint(NotificationCandidate::Type type)
{
switch (type)
{
case Notification::Type::UgcAuth: return "UgcAuth";
case Notification::Type::UgcReview: return "UgcReview";
case NotificationCandidate::Type::UgcAuth: return "UgcAuth";
case NotificationCandidate::Type::UgcReview: return "UgcReview";
}
}
} // namespace notifications

View file

@ -10,7 +10,6 @@
#include "base/logging.hpp"
#include <algorithm>
#include <chrono>
#include <memory>
#include <utility>
#include <vector>
@ -105,6 +104,12 @@ void Eye::UnsubscribeAll()
m_subscribers.clear();
}
// static
std::chrono::hours const & Eye::GetMapObjectEventsExpirePeriod()
{
return kMapObjectEventsExpirePeriod;
}
void Eye::TrimExpired()
{
GetPlatform().RunTask(Platform::Thread::File, [this]
@ -417,56 +422,47 @@ void Eye::Event::LayerShown(Layer::Type type)
}
// static
void Eye::Event::PlacePageOpened(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos)
void Eye::Event::PlacePageOpened(MapObject const & mapObject, m2::PointD const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, [bestType, pos, readableName, userPos]
GetPlatform().RunTask(Platform::Thread::File, [mapObject, userPos]
{
Instance().RegisterMapObjectEvent({bestType, pos, readableName}, MapObject::Event::Type::Open, userPos);
Instance().RegisterMapObjectEvent(mapObject, MapObject::Event::Type::Open, userPos);
});
}
// static
void Eye::Event::UgcEditorOpened(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos)
void Eye::Event::UgcEditorOpened(MapObject const & mapObject, m2::PointD const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, [bestType, pos, readableName, userPos]
GetPlatform().RunTask(Platform::Thread::File, [mapObject, userPos]
{
Instance().RegisterMapObjectEvent({bestType, pos, readableName}, MapObject::Event::Type::UgcEditorOpened,
userPos);
Instance().RegisterMapObjectEvent(mapObject, MapObject::Event::Type::UgcEditorOpened, userPos);
});
}
// static
void Eye::Event::UgcSaved(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos)
void Eye::Event::UgcSaved(MapObject const & mapObject, m2::PointD const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, [bestType, pos, readableName, userPos]
GetPlatform().RunTask(Platform::Thread::File, [mapObject, userPos]
{
Instance().RegisterMapObjectEvent({bestType, pos, readableName}, MapObject::Event::Type::UgcSaved,
userPos);
Instance().RegisterMapObjectEvent(mapObject, MapObject::Event::Type::UgcSaved, userPos);
});
}
// static
void Eye::Event::AddToBookmarkClicked(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos)
void Eye::Event::AddToBookmarkClicked(MapObject const & mapObject, m2::PointD const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, [bestType, pos, readableName, userPos]
GetPlatform().RunTask(Platform::Thread::File, [mapObject, userPos]
{
Instance().RegisterMapObjectEvent({bestType, pos, readableName}, MapObject::Event::Type::AddToBookmark,
userPos);
Instance().RegisterMapObjectEvent(mapObject, MapObject::Event::Type::AddToBookmark, userPos);
});
}
// static
void Eye::Event::RouteCreatedToObject(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos)
void Eye::Event::RouteCreatedToObject(MapObject const & mapObject, m2::PointD const & userPos)
{
GetPlatform().RunTask(Platform::Thread::File, [bestType, pos, readableName, userPos]
GetPlatform().RunTask(Platform::Thread::File, [mapObject, userPos]
{
Instance().RegisterMapObjectEvent({bestType, pos, readableName}, MapObject::Event::Type::RouteToCreated,
userPos);
Instance().RegisterMapObjectEvent(mapObject, MapObject::Event::Type::RouteToCreated, userPos);
});
}
} // namespace eye

View file

@ -5,6 +5,7 @@
#include "base/atomic_shared_ptr.hpp"
#include "base/macros.hpp"
#include <chrono>
#include <string>
#include <vector>
@ -44,16 +45,11 @@ public:
static void DiscoveryShown();
static void DiscoveryItemClicked(Discovery::Event event);
static void LayerShown(Layer::Type type);
static void PlacePageOpened(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos);
static void UgcEditorOpened(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos);
static void UgcSaved(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos);
static void AddToBookmarkClicked(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos);
static void RouteCreatedToObject(std::string const & bestType, m2::PointD const & pos,
std::string const & readableName, m2::PointD const & userPos);
static void PlacePageOpened(MapObject const & mapObject, m2::PointD const & userPos);
static void UgcEditorOpened(MapObject const & mapObject, m2::PointD const & userPos);
static void UgcSaved(MapObject const & mapObject, m2::PointD const & userPos);
static void AddToBookmarkClicked(MapObject const & mapObject, m2::PointD const & userPos);
static void RouteCreatedToObject(MapObject const & mapObject, m2::PointD const & userPos);
};
static Eye & Instance();
@ -64,6 +60,7 @@ public:
void Subscribe(Subscriber * subscriber);
void UnsubscribeAll();
static std::chrono::hours const & GetMapObjectEventsExpirePeriod();
void TrimExpired();
private:

View file

@ -219,6 +219,10 @@ public:
MercatorBounds::ClampX(pos.x + 1e-7), MercatorBounds::ClampY(pos.y + 1e-7)};
}
std::vector<std::string> const & GetMwmNames() const { return m_mwmNames; }
void SetMwmNames(std::vector<std::string> const & ids) { m_mwmNames = ids; }
std::string const & GetReadableName() const { return m_readableName; }
void SetReadableName(std::string const & readableName) { m_readableName = readableName; }
@ -230,11 +234,13 @@ public:
m2::RectD GetLimitRect() const { return m_limitRect; }
DECLARE_VISITOR(visitor(m_bestType, "type"), visitor(m_pos, "pos"),
visitor(m_readableName, "name"), visitor(m_events, "events"));
visitor(m_mwmNames, "mwm_names"), visitor(m_readableName, "name"),
visitor(m_events, "events"));
private:
std::string m_bestType;
m2::PointD m_pos;
std::vector<std::string> m_mwmNames;
std::string m_readableName;
// Mutable because of interface of the m4::Tree provides constant references in ForEach methods,
// but we need to add events into existing objects to avoid some overhead (copy + change +