diff --git a/map/bookmark.cpp b/map/bookmark.cpp index f8fd4a8975..3d373e7177 100644 --- a/map/bookmark.cpp +++ b/map/bookmark.cpp @@ -266,6 +266,16 @@ std::string BookmarkCategory::GetCatalogDeeplink() const return ss.str(); } +void BookmarkCategory::SetAuthor(std::string const & name, std::string const & id) +{ + if (m_data.m_authorName == name && m_data.m_authorId == id) + return; + + SetDirty(); + m_data.m_authorName = name; + m_data.m_authorId = id; +} + // static kml::PredefinedColor BookmarkCategory::GetDefaultColor() { diff --git a/map/bookmark.hpp b/map/bookmark.hpp index b265257d25..86b95b210a 100644 --- a/map/bookmark.hpp +++ b/map/bookmark.hpp @@ -88,6 +88,8 @@ public: bool IsCategoryFromCatalog() const; std::string GetCatalogDeeplink() const; + void SetAuthor(std::string const & name, std::string const & id); + private: void SetDirty() override; diff --git a/map/bookmark_manager.cpp b/map/bookmark_manager.cpp index f96160bc0b..0547bbab2d 100644 --- a/map/bookmark_manager.cpp +++ b/map/bookmark_manager.cpp @@ -3,6 +3,7 @@ #include "map/local_ads_mark.hpp" #include "map/routing_mark.hpp" #include "map/search_mark.hpp" +#include "map/user.hpp" #include "map/user_mark.hpp" #include "map/user_mark_id_storage.hpp" @@ -473,8 +474,9 @@ void FixUpHotelPlacemarks(BookmarkManager::KMLDataCollectionPtr & collection, using namespace std::placeholders; -BookmarkManager::BookmarkManager(Callbacks && callbacks) - : m_callbacks(std::move(callbacks)) +BookmarkManager::BookmarkManager(User & user, Callbacks && callbacks) + : m_user(user) + , m_callbacks(std::move(callbacks)) , m_changesTracker(*this) , m_needTeardown(false) , m_bookmarkCloud(Cloud::CloudParams("bmc.json", "bookmarks", std::string(kBookmarkCloudSettingsParam), @@ -498,6 +500,8 @@ BookmarkManager::BookmarkManager(Callbacks && callbacks) std::bind(&BookmarkManager::OnSynchronizationFinished, this, _1, _2, _3), std::bind(&BookmarkManager::OnRestoreRequested, this, _1, _2, _3), std::bind(&BookmarkManager::OnRestoredFilesPrepared, this)); + + m_bookmarkCloud.SetInvalidTokenHandler([this] { m_user.ResetAccessToken(); }); } BookmarkManager::EditSession BookmarkManager::GetEditSession() @@ -1346,11 +1350,8 @@ void BookmarkManager::UpdateBmGroupIdList() kml::MarkGroupId BookmarkManager::CreateBookmarkCategory(kml::CategoryData && data, bool autoSave) { CHECK_THREAD_CHECKER(m_threadChecker, ()); - if (data.m_id == kml::kInvalidMarkGroupId) - { data.m_id = UserMarkIdStorage::Instance().GetNextCategoryId(); - } auto groupId = data.m_id; CHECK_EQUAL(m_categories.count(groupId), 0, ()); m_categories[groupId] = my::make_unique(std::move(data), autoSave); @@ -1365,6 +1366,7 @@ kml::MarkGroupId BookmarkManager::CreateBookmarkCategory(std::string const & nam auto const groupId = UserMarkIdStorage::Instance().GetNextCategoryId(); CHECK_EQUAL(m_categories.count(groupId), 0, ()); m_categories[groupId] = my::make_unique(name, groupId, autoSave); + m_categories[groupId]->SetAuthor(m_user.GetUserName(), m_user.GetUserId()); UpdateBmGroupIdList(); m_changesTracker.OnAddGroup(groupId); return groupId; @@ -1732,11 +1734,6 @@ std::unique_ptr BookmarkManager::GetUserSubscriber() return m_bookmarkCloud.GetUserSubscriber(); } -void BookmarkManager::SetInvalidTokenHandler(Cloud::InvalidTokenHandler && onInvalidToken) -{ - m_bookmarkCloud.SetInvalidTokenHandler(std::move(onInvalidToken)); -} - void BookmarkManager::PrepareFileForSharing(kml::MarkGroupId categoryId, SharingHandler && handler) { CHECK_THREAD_CHECKER(m_threadChecker, ()); diff --git a/map/bookmark_manager.hpp b/map/bookmark_manager.hpp index b8c672a982..de83691bc0 100644 --- a/map/bookmark_manager.hpp +++ b/map/bookmark_manager.hpp @@ -28,6 +28,8 @@ #include +class User; + class BookmarkManager final { using UserMarkLayers = std::vector>; @@ -78,7 +80,7 @@ public: class EditSession { public: - EditSession(BookmarkManager & bmManager); + explicit EditSession(BookmarkManager & bmManager); ~EditSession(); template @@ -131,7 +133,7 @@ public: BookmarkManager & m_bmManager; }; - explicit BookmarkManager(Callbacks && callbacks); + BookmarkManager(User & user, Callbacks && callbacks); void SetDrapeEngine(ref_ptr engine); @@ -207,7 +209,6 @@ public: bool IsCloudEnabled() const; uint64_t GetLastSynchronizationTimestampInMs() const; std::unique_ptr GetUserSubscriber(); - void SetInvalidTokenHandler(Cloud::InvalidTokenHandler && onInvalidToken); enum class CategoryFilterType { @@ -483,6 +484,7 @@ private: bool CheckVisibility(CategoryFilterType const filter, bool isVisible) const; ThreadChecker m_threadChecker; + User & m_user; Callbacks m_callbacks; MarksChangesTracker m_changesTracker; df::DrapeEngineSafePtr m_drapeEngine; diff --git a/map/framework.cpp b/map/framework.cpp index 97e136d465..4cae3a1207 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -428,7 +428,7 @@ Framework::Framework(FrameworkParams const & params) InitSearchAPI(); LOG(LDEBUG, ("Search API initialized")); - m_bmManager = make_unique(BookmarkManager::Callbacks( + m_bmManager = make_unique(m_user, BookmarkManager::Callbacks( [this]() -> StringsBundle const & { return m_stringsBundle; }, [this](vector> const & marks) { GetSearchAPI().OnBookmarksCreated(marks); @@ -442,7 +442,6 @@ Framework::Framework(FrameworkParams const & params) m_routingManager.SetBookmarkManager(m_bmManager.get()); m_searchMarks.SetBookmarkManager(m_bmManager.get()); - m_bmManager->SetInvalidTokenHandler([this] { m_user.ResetAccessToken(); }); m_user.AddSubscriber(m_bmManager->GetUserSubscriber()); m_routingManager.SetTransitManager(&m_transitManager); @@ -513,9 +512,9 @@ Framework::~Framework() DestroyDrapeEngine(); m_model.SetOnMapDeregisteredCallback(nullptr); - m_bmManager->SetInvalidTokenHandler(nullptr); - m_user.ClearSubscribers(); + // Must be destroyed implicitly since it stores reference to m_user. + m_bmManager.reset(); } booking::Api * Framework::GetBookingApi(platform::NetworkPolicy const & policy) diff --git a/map/user.cpp b/map/user.cpp index 7f73ca2bd1..175f2d367a 100644 --- a/map/user.cpp +++ b/map/user.cpp @@ -30,6 +30,9 @@ namespace { std::string const kMapsMeTokenKey = "MapsMeToken"; std::string const kReviewIdsKey = "UserReviewIds"; +std::string const kUserNameKey = "MapsMeUserName"; +std::string const kUserIdKey = "MapsMeUserId"; +std::string const kDefaultUserName = "Anonymous Traveller"; std::string const kPassportServerUrl = PASSPORT_URL; std::string const kAppName = PASSPORT_APP_NAME; std::string const kUGCServerUrl = UGC_URL; @@ -72,6 +75,13 @@ std::string AuthenticationUrl(std::string const & socialToken, } std::string UserDetailsUrl() +{ + if (kPassportServerUrl.empty()) + return {}; + return kPassportServerUrl + "/user_details"; +} + +std::string UserReviewsUrl() { if (kUGCServerUrl.empty()) return {}; @@ -165,6 +175,17 @@ struct SocialNetworkAuthRequestData visitor(m_promoAccepted, "promo_accepted")) }; +struct UserDetailsResponseData +{ + std::string m_firstName; + std::string m_lastName; + std::string m_userId; + + DECLARE_VISITOR(visitor(m_firstName, "first_name"), + visitor(m_lastName, "last_name"), + visitor(m_userId, "keyid")) +}; + template std::string SerializeToJson(DataType const & data) { @@ -175,6 +196,13 @@ std::string SerializeToJson(DataType const & data) serializer(data); return jsonStr; } + +template +void DeserializeFromJson(std::string const & jsonStr, DataType & result) +{ + coding::DeserializerJson des(jsonStr); + des(result); +} } // namespace User::User() @@ -186,14 +214,24 @@ void User::Init() { std::lock_guard lock(m_mutex); + auto & secureStorage = GetPlatform().GetSecureStorage(); + std::string token; - if (GetPlatform().GetSecureStorage().Load(kMapsMeTokenKey, token)) + if (secureStorage.Load(kMapsMeTokenKey, token)) m_accessToken = token; + std::string userName; + if (secureStorage.Load(kUserNameKey, userName)) + m_userName = userName; + + std::string userId; + if (secureStorage.Load(kUserIdKey, userId)) + m_userId = userId; + NotifySubscribersImpl(); std::string reviewIds; - if (GetPlatform().GetSecureStorage().Load(kReviewIdsKey, reviewIds)) + if (secureStorage.Load(kReviewIdsKey, reviewIds)) { m_details.m_reviewIds = DeserializeReviewIds(reviewIds); std::sort(m_details.m_reviewIds.begin(), m_details.m_reviewIds.end()); @@ -234,6 +272,18 @@ std::string User::GetAccessToken() const return m_accessToken; } +std::string User::GetUserName() const +{ + std::lock_guard lock(m_mutex); + return m_userName; +} + +std::string User::GetUserId() const +{ + std::lock_guard lock(m_mutex); + return m_userId; +} + User::Details User::GetDetails() const { std::lock_guard lock(m_mutex); @@ -363,19 +413,53 @@ void User::ClearSubscribersImpl() void User::RequestUserDetails() { - std::string const url = UserDetailsUrl(); - if (url.empty()) + std::string const detailsUrl = UserDetailsUrl(); + if (detailsUrl.empty()) { LOG(LWARNING, ("User details service is unavailable.")); return; } + std::string const reviewsUrl = UserReviewsUrl(); + if (reviewsUrl.empty()) + { + LOG(LWARNING, ("User reviews service is unavailable.")); + return; + } + if (m_accessToken.empty()) return; - GetPlatform().RunTask(Platform::Thread::Network, [this, url]() + GetPlatform().RunTask(Platform::Thread::Network, [this, detailsUrl, reviewsUrl]() { - Request(url, [this](platform::HttpClient & request) + // Request user details. + Request(detailsUrl, [this](platform::HttpClient & request) + { + request.SetRawHeader("Authorization", BuildAuthorizationToken(m_accessToken)); + }, + [this](std::string const & response) + { + UserDetailsResponseData userDetails; + DeserializeFromJson(response, userDetails); + + auto userName = kDefaultUserName; + if (!userDetails.m_firstName.empty() && !userDetails.m_lastName.empty()) + userName = userDetails.m_firstName + " " + userDetails.m_lastName; + else if (!userDetails.m_firstName.empty()) + userName = userDetails.m_firstName; + else if (!userDetails.m_lastName.empty()) + userName = userDetails.m_lastName; + + GetPlatform().GetSecureStorage().Save(kUserNameKey, userName); + GetPlatform().GetSecureStorage().Save(kUserIdKey, userDetails.m_userId); + + std::lock_guard lock(m_mutex); + m_userName = userName; + m_userId = userDetails.m_userId; + }); + + // Request user's reviews. + Request(reviewsUrl, [this](platform::HttpClient & request) { request.SetRawHeader("Authorization", BuildAuthorizationToken(m_accessToken)); }, diff --git a/map/user.hpp b/map/user.hpp index 0e9e9438f4..596355a23a 100644 --- a/map/user.hpp +++ b/map/user.hpp @@ -60,6 +60,8 @@ public: void ClearSubscribers(); std::string GetAccessToken() const; + std::string GetUserName() const; + std::string GetUserId() const; Details GetDetails() const; void UploadUserReviews(std::string && dataStr, size_t numberOfUnsynchronized, @@ -87,6 +89,8 @@ private: void FinishAuthentication(); std::string m_accessToken; + std::string m_userName; + std::string m_userId; mutable std::mutex m_mutex; bool m_authenticationInProgress = false; Details m_details;