diff --git a/android/jni/app/organicmaps/Framework.cpp b/android/jni/app/organicmaps/Framework.cpp index ad17613965..209e0b1608 100644 --- a/android/jni/app/organicmaps/Framework.cpp +++ b/android/jni/app/organicmaps/Framework.cpp @@ -1650,6 +1650,16 @@ Java_app_organicmaps_Framework_nativeDeleteBookmarkFromMapObject(JNIEnv * env, j return usermark_helper::CreateMapObject(env, g_framework->GetPlacePageInfo()); } +JNIEXPORT jstring JNICALL +Java_app_organicmaps_Framework_nativeGetPoiContactUrl(JNIEnv *env, jclass, jint id) +{ + auto const metaID = static_cast(id); + string_view const value = g_framework->GetPlacePageInfo().GetMetadata(metaID); + if (osm::isSocialContactTag(metaID)) + return jni::ToJavaString(env, osm::socialContactToURL(metaID, value)); + return jni::ToJavaString(env, value); +} + JNIEXPORT void JNICALL Java_app_organicmaps_Framework_nativeTurnOnChoosePositionMode(JNIEnv *, jclass, jboolean isBusiness, jboolean applyPosition) { diff --git a/android/jni/app/organicmaps/editor/Editor.cpp b/android/jni/app/organicmaps/editor/Editor.cpp index cbe58bb013..c4b295822f 100644 --- a/android/jni/app/organicmaps/editor/Editor.cpp +++ b/android/jni/app/organicmaps/editor/Editor.cpp @@ -100,6 +100,14 @@ Java_app_organicmaps_editor_Editor_nativeGetMetadata(JNIEnv * env, jclass, jint { auto const metaID = static_cast(id); ASSERT_LESS(metaID, osm::MapObject::MetadataID::FMD_COUNT, ()); + if (osm::isSocialContactTag(metaID)) + { + auto const value = g_editableMapObject.GetMetadata(metaID); + if (value.find('/') == std::string::npos) // `value` contains pagename. + return jni::ToJavaString(env, value); + // `value` contains URL. + return jni::ToJavaString(env, osm::socialContactToURL(metaID, value)); + } return jni::ToJavaString(env, g_editableMapObject.GetMetadata(metaID)); } diff --git a/android/src/app/organicmaps/Framework.java b/android/src/app/organicmaps/Framework.java index 05fbae2eae..9b39a3704c 100644 --- a/android/src/app/organicmaps/Framework.java +++ b/android/src/app/organicmaps/Framework.java @@ -335,13 +335,12 @@ public class Framework public static native boolean nativeIsIsolinesLayerEnabled(); - public static native void nativeSetGuidesLayerEnabled(boolean enabled); - - public static native boolean nativeIsGuidesLayerEnabled(); - @NonNull public static native MapObject nativeDeleteBookmarkFromMapObject(); + @NonNull + public static native String nativeGetPoiContactUrl(int metadataType); + public static native void nativeZoomToPoint(double lat, double lon, int zoom, boolean animate); /** diff --git a/android/src/app/organicmaps/editor/Editor.java b/android/src/app/organicmaps/editor/Editor.java index 19a303d9c6..66cb1e7691 100644 --- a/android/src/app/organicmaps/editor/Editor.java +++ b/android/src/app/organicmaps/editor/Editor.java @@ -72,7 +72,6 @@ public final class Editor public static native String nativeGetMetadata(int id); public static native boolean nativeIsMetadataValid(int id, String value); public static native void nativeSetMetadata(int id, String value); - public static native String nativeGetOpeningHours(); public static native void nativeSetOpeningHours(String openingHours); public static String nativeGetPhone() diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageLinksFragment.java b/android/src/app/organicmaps/widget/placepage/PlacePageLinksFragment.java index bc70897337..d43f7dc08b 100644 --- a/android/src/app/organicmaps/widget/placepage/PlacePageLinksFragment.java +++ b/android/src/app/organicmaps/widget/placepage/PlacePageLinksFragment.java @@ -12,6 +12,7 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; +import app.organicmaps.Framework; import app.organicmaps.R; import app.organicmaps.bookmarks.data.MapObject; import app.organicmaps.bookmarks.data.Metadata; @@ -145,7 +146,7 @@ public class PlacePageLinksFragment extends Fragment implements Observer= 0) - refreshMetadataOrHide("https://" + webDomain + "/" + socialPage, view, tvSocialPage); - else - refreshMetadataOrHide("@" + socialPage, view, tvSocialPage); - } - - private void refreshSocialPageLink(View view, TextView tvSocialPage, String socialPage) - { - if (TextUtils.isEmpty(socialPage)) - view.setVisibility(GONE); - else if (socialPage.indexOf('/') >= 0) - refreshMetadataOrHide("https://" + socialPage, view, tvSocialPage); - else - refreshMetadataOrHide("@" + socialPage, view, tvSocialPage); + final String socialPage = mapObject.getMetadata(metaType); + refreshMetadataOrHide(socialPage, view, tvSocialPage); } private static String getWebsiteUrl(MapObject mapObject) @@ -229,15 +202,6 @@ public class PlacePageLinksFragment extends Fragment implements Observer + @@ -167,6 +168,7 @@ + diff --git a/editor/editor_tests/editor_config_test.cpp b/editor/editor_tests/editor_config_test.cpp index d2932e5d81..e609043f06 100644 --- a/editor/editor_tests/editor_config_test.cpp +++ b/editor/editor_tests/editor_config_test.cpp @@ -21,6 +21,7 @@ UNIT_TEST(EditorConfig_TypeDescription) EType::FMD_CONTACT_INSTAGRAM, EType::FMD_CONTACT_TWITTER, EType::FMD_CONTACT_VK, + EType::FMD_CONTACT_LINE, }; pugi::xml_document doc; diff --git a/editor/editor_tests/xml_feature_test.cpp b/editor/editor_tests/xml_feature_test.cpp index 5764d7d208..dac94b0812 100644 --- a/editor/editor_tests/xml_feature_test.cpp +++ b/editor/editor_tests/xml_feature_test.cpp @@ -480,3 +480,89 @@ UNIT_TEST(XMLFeature_Diet) ft.SetCuisine(""); TEST_EQUAL(ft.GetCuisine(), "", ()); } + +UNIT_TEST(XMLFeature_SocialContactsProcessing) +{ + { + std::string const nightclubStr = R"( + + + + + + + + )"; + + editor::XMLFeature xmlFeature(nightclubStr); + + osm::EditableMapObject emo; + editor::FromXML(xmlFeature, emo); + + // Read and write "contact:facebook" to apply normalization. + std::string contactFacebook(emo.GetMetadata(feature::Metadata::FMD_CONTACT_FACEBOOK)); + emo.SetMetadata(osm::MapObject::MetadataID::FMD_CONTACT_FACEBOOK, contactFacebook); + + // Read and write "contact:instagram" to apply normalization. + std::string contactInstagram(emo.GetMetadata(feature::Metadata::FMD_CONTACT_INSTAGRAM)); + emo.SetMetadata(osm::MapObject::MetadataID::FMD_CONTACT_INSTAGRAM, contactInstagram); + + // Read and write "contact:line" to apply normalization. + std::string contactLine(emo.GetMetadata(feature::Metadata::FMD_CONTACT_LINE)); + emo.SetMetadata(osm::MapObject::MetadataID::FMD_CONTACT_LINE, contactLine); + + auto convertedFt = editor::ToXML(emo, true); + + TEST(convertedFt.HasTag("contact:facebook"), ()); + TEST_EQUAL(convertedFt.GetTagValue("contact:facebook"), "https://facebook.com/pages/Stereo-Plaza/118100041593935", ()); + + TEST(convertedFt.HasTag("contact:instagram"), ()); + TEST_EQUAL(convertedFt.GetTagValue("contact:instagram"), "https://instagram.com/p/CSy87IhMhfm", ()); + + TEST(convertedFt.HasTag("contact:line"), ()); + TEST_EQUAL(convertedFt.GetTagValue("contact:line"), "https://liff.line.me/1645278921-kWRPP32q/?accountId=673watcr", ()); + } +} + +UNIT_TEST(XMLFeature_SocialContactsProcessing_clean) +{ + { + std::string const nightclubStr = R"( + + + + + + + + )"; + + editor::XMLFeature xmlFeature(nightclubStr); + + osm::EditableMapObject emo; + editor::FromXML(xmlFeature, emo); + + // Read and write "contact:facebook" to apply normalization. + std::string contactFacebook(emo.GetMetadata(feature::Metadata::FMD_CONTACT_FACEBOOK)); + emo.SetMetadata(osm::MapObject::MetadataID::FMD_CONTACT_FACEBOOK, contactFacebook); + + // Read and write "contact:instagram" to apply normalization. + std::string contactInstagram(emo.GetMetadata(feature::Metadata::FMD_CONTACT_INSTAGRAM)); + emo.SetMetadata(osm::MapObject::MetadataID::FMD_CONTACT_INSTAGRAM, contactInstagram); + + // Read and write "contact:line" to apply normalization. + std::string contactLine(emo.GetMetadata(feature::Metadata::FMD_CONTACT_LINE)); + emo.SetMetadata(osm::MapObject::MetadataID::FMD_CONTACT_LINE, contactLine); + + auto convertedFt = editor::ToXML(emo, true); + + TEST(convertedFt.HasTag("contact:facebook"), ()); + TEST_EQUAL(convertedFt.GetTagValue("contact:facebook"), "PierreCardinPeru.oficial", ()); + + TEST(convertedFt.HasTag("contact:instagram"), ()); + TEST_EQUAL(convertedFt.GetTagValue("contact:instagram"), "fraback.genusswelt", ()); + + TEST(convertedFt.HasTag("contact:line"), ()); + TEST_EQUAL(convertedFt.GetTagValue("contact:line"), "015qevdv", ()); + } +} diff --git a/editor/xml_feature.cpp b/editor/xml_feature.cpp index 5ac1b1ba85..51021c8376 100644 --- a/editor/xml_feature.cpp +++ b/editor/xml_feature.cpp @@ -3,6 +3,7 @@ #include "indexer/classificator.hpp" #include "indexer/editable_map_object.hpp" #include "indexer/ftypes_matcher.hpp" +#include "indexer/validate_and_format_contacts.hpp" #include "coding/string_utf8_multilang.hpp" @@ -606,7 +607,10 @@ XMLFeature ToXML(osm::EditableMapObject const & object, bool serializeType) object.ForEachMetadataItem([&toFeature](string_view tag, string_view value) { - toFeature.SetTagValue(tag, value); + if (osm::isSocialContactTag(tag) && value.find('/') != std::string::npos) + toFeature.SetTagValue(tag, osm::socialContactToURL(tag, value)); + else + toFeature.SetTagValue(tag, value); }); return toFeature; diff --git a/indexer/indexer_tests/validate_and_format_contacts_test.cpp b/indexer/indexer_tests/validate_and_format_contacts_test.cpp index ea8212f9bf..536876effb 100644 --- a/indexer/indexer_tests/validate_and_format_contacts_test.cpp +++ b/indexer/indexer_tests/validate_and_format_contacts_test.cpp @@ -9,11 +9,15 @@ UNIT_TEST(EditableMapObject_ValidateAndFormat_facebook) TEST_EQUAL(osm::ValidateAndFormat_facebook(""), "", ()); TEST_EQUAL(osm::ValidateAndFormat_facebook("facebook.com/OpenStreetMap"), "OpenStreetMap", ()); TEST_EQUAL(osm::ValidateAndFormat_facebook("www.facebook.com/OpenStreetMap"), "OpenStreetMap", ()); + TEST_EQUAL(osm::ValidateAndFormat_facebook("www.facebook.fr/OpenStreetMap"), "OpenStreetMap", ()); TEST_EQUAL(osm::ValidateAndFormat_facebook("http://facebook.com/OpenStreetMap"), "OpenStreetMap", ()); TEST_EQUAL(osm::ValidateAndFormat_facebook("https://facebook.com/OpenStreetMap"), "OpenStreetMap", ()); TEST_EQUAL(osm::ValidateAndFormat_facebook("http://www.facebook.com/OpenStreetMap"), "OpenStreetMap", ()); TEST_EQUAL(osm::ValidateAndFormat_facebook("https://www.facebook.com/OpenStreetMap"), "OpenStreetMap", ()); + TEST_EQUAL(osm::ValidateAndFormat_facebook("https://de-de.facebook.de/Open_Street_Map"), "Open_Street_Map", ()); TEST_EQUAL(osm::ValidateAndFormat_facebook("https://en-us.facebook.com/OpenStreetMap"), "OpenStreetMap", ()); + TEST_EQUAL(osm::ValidateAndFormat_facebook("https://de-de.facebook.com/profile.php?id=100085707580841"), "100085707580841", ()); + TEST_EQUAL(osm::ValidateAndFormat_facebook("http://facebook.com/profile.php?share=app&id=100086487430889#m"), "100086487430889", ()); TEST_EQUAL(osm::ValidateAndFormat_facebook("some.good.page"), "some.good.page", ()); TEST_EQUAL(osm::ValidateAndFormat_facebook("@tree-house-interiors"), "tree-house-interiors", ()); @@ -61,14 +65,16 @@ UNIT_TEST(EditableMapObject_ValidateAndFormat_twitter) UNIT_TEST(EditableMapObject_ValidateAndFormat_vk) { TEST_EQUAL(osm::ValidateAndFormat_vk("vk.com/id404"), "id404", ()); - TEST_EQUAL(osm::ValidateAndFormat_vk("vkontakte.ru/id404"), "id404", ()); + TEST_EQUAL(osm::ValidateAndFormat_vk("vkontakte.ru/id4321"), "id4321", ()); + TEST_EQUAL(osm::ValidateAndFormat_vk("www.vkontakte.ru/id43210"), "id43210", ()); TEST_EQUAL(osm::ValidateAndFormat_vk("www.vk.com/id404"), "id404", ()); - TEST_EQUAL(osm::ValidateAndFormat_vk("http://vk.com/id404"), "id404", ()); - TEST_EQUAL(osm::ValidateAndFormat_vk("https://vk.com/id404"), "id404", ()); - TEST_EQUAL(osm::ValidateAndFormat_vk("https://vkontakte.ru/id404"), "id404", ()); - TEST_EQUAL(osm::ValidateAndFormat_vk("http://www.vk.com/id404"), "id404", ()); - TEST_EQUAL(osm::ValidateAndFormat_vk("https://www.vk.com/id404"), "id404", ()); - TEST_EQUAL(osm::ValidateAndFormat_vk("https://www.vk.com/id405/"), "id405", ()); + TEST_EQUAL(osm::ValidateAndFormat_vk("http://vk.com/ozon"), "ozon", ()); + TEST_EQUAL(osm::ValidateAndFormat_vk("https://vk.com/sklad169"), "sklad169", ()); + TEST_EQUAL(osm::ValidateAndFormat_vk("https://vkontakte.ru/id4321"), "id4321", ()); + TEST_EQUAL(osm::ValidateAndFormat_vk("https://www.vkontakte.ru/id4321"), "id4321", ()); + TEST_EQUAL(osm::ValidateAndFormat_vk("http://www.vk.com/ugona.net.expert"), "ugona.net.expert", ()); + TEST_EQUAL(osm::ValidateAndFormat_vk("https://www.vk.com/7cvetov18"), "7cvetov18", ()); + TEST_EQUAL(osm::ValidateAndFormat_vk("https://www.vk.com/7cvetov18/"), "7cvetov18", ()); TEST_EQUAL(osm::ValidateAndFormat_vk("@22ab.cdef"), "22ab.cdef", ()); TEST_EQUAL(osm::ValidateAndFormat_vk("instagram.com/hello_world"), "", ()); @@ -81,6 +87,7 @@ UNIT_TEST(EditableMapObject_ValidateAndFormat_contactLine) TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://line.me/ti/p/@dgxs9r6wad"), "dgxs9r6wad", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://line.me/ti/p/%40vne5uwke17"), "vne5uwke17", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("http://line.me/R/ti/p/bfsg1a8x9u"), "bfsg1a8x9u", ()); + TEST_EQUAL(osm::ValidateAndFormat_contactLine("line.me/R/ti/p/bfsg1a8x9u"), "bfsg1a8x9u", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://line.me/R/ti/p/gdltt7s380"), "gdltt7s380", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://line.me/R/ti/p/@sdb2pb3lsg"), "sdb2pb3lsg", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://line.me/R/ti/p/%40b30h5mdj11"), "b30h5mdj11", ()); @@ -91,6 +98,9 @@ UNIT_TEST(EditableMapObject_ValidateAndFormat_contactLine) TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://line.me/R/home/public/profile?id=r90ck7n1rq"), "r90ck7n1rq", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://page.line.me/fom5198h"), "fom5198h", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://page.line.me/qn58n8g?web=mobile"), "qn58n8g", ()); + TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://page.line.me/?accountId=673watcr"), "673watcr", ()); + TEST_EQUAL(osm::ValidateAndFormat_contactLine("page.line.me/?accountId=673watcr"), "673watcr", ()); + TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://liff.line.me/1645278921-kWRPP32q/?accountId=673watcr"), "liff.line.me/1645278921-kWRPP32q/?accountId=673watcr", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("https://abc.line.me/en/some/page?id=xaladqv"), "abc.line.me/en/some/page?id=xaladqv", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("@abcd"), "abcd", ()); TEST_EQUAL(osm::ValidateAndFormat_contactLine("@-hyphen-test-"), "-hyphen-test-", ()); @@ -103,11 +113,14 @@ UNIT_TEST(EditableMapObject_ValidateFacebookPage) TEST(osm::ValidateFacebookPage(""), ()); TEST(osm::ValidateFacebookPage("facebook.com/OpenStreetMap"), ()); TEST(osm::ValidateFacebookPage("www.facebook.com/OpenStreetMap"), ()); + TEST(osm::ValidateFacebookPage("www.facebook.fr/OpenStreetMap"), ()); TEST(osm::ValidateFacebookPage("http://facebook.com/OpenStreetMap"), ()); TEST(osm::ValidateFacebookPage("https://facebook.com/OpenStreetMap"), ()); TEST(osm::ValidateFacebookPage("http://www.facebook.com/OpenStreetMap"), ()); TEST(osm::ValidateFacebookPage("https://www.facebook.com/OpenStreetMap"), ()); + TEST(osm::ValidateFacebookPage("https://de-de.facebook.de/OpenStreetMap"), ()); TEST(osm::ValidateFacebookPage("https://en-us.facebook.com/OpenStreetMap"), ()); + TEST(osm::ValidateFacebookPage("https://www.facebook.com/profile.php?id=100085707580841"), ()); TEST(osm::ValidateFacebookPage("OpenStreetMap"), ()); TEST(osm::ValidateFacebookPage("some.good.page"), ()); TEST(osm::ValidateFacebookPage("Quaama-Volunteer-Bushfire-Brigade-526790054021506"), ()); @@ -248,3 +261,18 @@ UNIT_TEST(EditableMapObject_ValidateLinePage) TEST(!osm::ValidateLinePage("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), ()); TEST(!osm::ValidateLinePage("https://line.com/ti/p/invalid-domain"), ()); } + +UNIT_TEST(EditableMapObject_socialContactToURL) +{ + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_INSTAGRAM, "some_page_name"), "https://instagram.com/some_page_name", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_INSTAGRAM, "p/BvkgKZNDbqN"), "https://instagram.com/p/BvkgKZNDbqN", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_FACEBOOK, "100086487430889"), "https://facebook.com/100086487430889", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_FACEBOOK, "nova.poshta.official"), "https://facebook.com/nova.poshta.official", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_FACEBOOK, "pg/ESQ-336537783591903/about"), "https://facebook.com/pg/ESQ-336537783591903/about", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_TWITTER, "carmelopizza"), "https://twitter.com/carmelopizza", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_TWITTER, "demhamburguesa/status/688001869269078016"), "https://twitter.com/demhamburguesa/status/688001869269078016", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_VK, "beerhousebar"), "https://vk.com/beerhousebar", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_VK, "wall-41524_29351"), "https://vk.com/wall-41524_29351", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_LINE, "a26235875"), "https://line.me/R/ti/p/@a26235875", ()); + TEST_EQUAL(osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_LINE, "liff.line.me/1645278921-kWRPP32q/?accountId=673watcr"), "https://liff.line.me/1645278921-kWRPP32q/?accountId=673watcr", ()); +} \ No newline at end of file diff --git a/indexer/validate_and_format_contacts.cpp b/indexer/validate_and_format_contacts.cpp index 00c4ee462d..f544a452bb 100644 --- a/indexer/validate_and_format_contacts.cpp +++ b/indexer/validate_and_format_contacts.cpp @@ -17,22 +17,50 @@ static auto const s_badVkRegex = regex(R"(^\d\d\d.+$)"); static auto const s_goodVkRegex = regex(R"(^[A-Za-z0-9_.]{5,32}$)"); static auto const s_lineRegex = regex(R"(^[a-z0-9-_.]{4,20}$)"); -char const * const kWebsiteProtocols[] = {"http://", "https://"}; -size_t const kWebsiteProtocolDefaultIndex = 0; +constexpr string_view kFacebook{"contact:facebook"}; +constexpr string_view kInstagram{"contact:instagram"}; +constexpr string_view kTwitter{"contact:twitter"}; +constexpr string_view kVk{"contact:vk"}; +constexpr string_view kLine{"contact:line"}; + +constexpr string_view kProfilePhp{"profile.php"}; + +// Domains constants. +constexpr string_view kFbDot{"fb."}; +constexpr string_view kFacebookDot{"facebook."}; +constexpr string_view kInstagramCom{"instagram.com"}; +constexpr string_view kDotInstagramCom{".instagram.com"}; +constexpr string_view kTwitterCom{"twitter.com"}; +constexpr string_view kDotTwitterCom{".twitter.com"}; +constexpr string_view kVkCom{"vk.com"}; +constexpr string_view kVkontakteRu{"vkontakte.ru"}; +constexpr string_view kDotVkCom{".vk.com"}; +constexpr string_view kDotVkontakteRu{".vkontakte.ru"}; +constexpr string_view kLineMe{"line.me"}; +constexpr string_view kPageLineMe{"page.line.me"}; +constexpr string_view kDotLineMe{".line.me"}; + +// URLs constants +constexpr string_view kUrlFacebook{"https://facebook.com/"}; +constexpr string_view kUrlInstagram{"https://instagram.com/"}; +constexpr string_view kUrlTwitter{"https://twitter.com/"}; +constexpr string_view kUrlVk{"https://vk.com/"}; +constexpr string_view kUrlLine{"https://line.me/R/ti/p/@"}; +constexpr string_view kHttp{"http://"}; +constexpr string_view kHttps{"https://"}; size_t GetProtocolNameLength(string const & website) { - for (auto const & protocol : kWebsiteProtocols) - { - if (strings::StartsWith(website, protocol)) - return strlen(protocol); - } + if (strings::StartsWith(website, kHttp)) + return kHttp.size(); + if (strings::StartsWith(website, kHttps)) + return kHttps.size(); return 0; } bool IsProtocolSpecified(string const & website) { - return GetProtocolNameLength(website) > 0; + return strings::StartsWith(website, kHttp) || strings::StartsWith(website, kHttps); } // TODO: Current implementation looks only for restricted symbols from ASCII block ignoring @@ -60,7 +88,7 @@ bool containsInvalidFBSymbol(string const & facebookPage, size_t startIndex = 0) std::string ValidateAndFormat_website(std::string const & v) { if (!v.empty() && !IsProtocolSpecified(v)) - return kWebsiteProtocols[kWebsiteProtocolDefaultIndex] + v; + return string{kHttp}.append(v); return v; } @@ -90,12 +118,18 @@ string ValidateAndFormat_facebook(string const & facebookPage) url::Url const url = url::Url::FromString(facebookPage); string const domain = strings::MakeLowerCase(url.GetHost()); // Check Facebook domain name. - if (strings::EndsWith(domain, "facebook.com") || strings::EndsWith(domain, "fb.com") - || strings::EndsWith(domain, "fb.me") || strings::EndsWith(domain, "facebook.de") - || strings::EndsWith(domain, "facebook.fr")) + if (strings::StartsWith(domain, kFacebookDot) || strings::StartsWith(domain, kFbDot) || + domain.find(".facebook.") != string::npos || domain.find(".fb.") != string::npos) { auto webPath = url.GetPath(); - // Strip last '/' symbol + // In case of https://www.facebook.com/profile.php?id=100085707580841 extract only ID. + if (strings::StartsWith(webPath, kProfilePhp)) + { + std::string const * id = url.GetParamValue("id"); + return (id ? *id : std::string()); + } + + // Strip last '/' symbol. webPath.erase(webPath.find_last_not_of('/') + 1); return webPath; } @@ -120,8 +154,8 @@ string ValidateAndFormat_instagram(string const & instagramPage) url::Url const url = url::Url::FromString(instagramPage); string const domain = strings::MakeLowerCase(url.GetHost()); - // Check Instagram domain name. - if (domain == "instagram.com" || strings::EndsWith(domain, ".instagram.com")) + // Check Instagram domain name: "instagram.com" or "*.instagram.com". + if (domain == kInstagramCom || strings::EndsWith(domain, kDotInstagramCom)) { auto webPath = url.GetPath(); // Strip last '/' symbol. @@ -149,12 +183,12 @@ string ValidateAndFormat_twitter(string const & twitterPage) url::Url const url = url::Url::FromString(twitterPage); string const domain = strings::MakeLowerCase(url.GetHost()); - // Check Twitter domain name. - if (domain == "twitter.com" || strings::EndsWith(domain, ".twitter.com")) + // Check Twitter domain name: "twitter.com" or "*.twitter.com". + if (domain == kTwitterCom || strings::EndsWith(domain, kDotTwitterCom)) { auto webPath = url.GetPath(); - // Strip last '/' symbol and first '@' symbol + // Strip last '/' symbol and first '@' symbol. webPath.erase(webPath.find_last_not_of('/') + 1); webPath.erase(0, webPath.find_first_not_of('@')); @@ -190,9 +224,9 @@ string ValidateAndFormat_vk(string const & vkPage) url::Url const url = url::Url::FromString(vkPage); string const domain = strings::MakeLowerCase(url.GetHost()); - // Check VK domain name. - if (domain == "vk.com" || strings::EndsWith(domain, ".vk.com") || - domain == "vkontakte.ru" || strings::EndsWith(domain, ".vkontakte.ru")) + // Check VK domain name: "vk.com" or "vkontakte.ru" or "*.vk.com" or "*.vkontakte.ru". + if (domain == kVkCom || strings::EndsWith(domain, kDotVkCom) || + domain == kVkontakteRu || strings::EndsWith(domain, kDotVkontakteRu)) { auto webPath = url.GetPath(); // Strip last '/' symbol. @@ -239,13 +273,18 @@ string ValidateAndFormat_contactLine(string const & linePage) url::Url const url = url::Url::FromString(linePage); string const domain = strings::MakeLowerCase(url.GetHost()); // Check Line domain name. - if (domain == "page.line.me") + if (domain == kPageLineMe) { + // Parse https://page.line.me/?accountId={LINE ID} + std::string const * id = url.GetParamValue("accountId"); + if (id != nullptr) + return *id; + // Parse https://page.line.me/{LINE ID} string lineId = url.GetPath(); return stripAtSymbol(lineId); } - else if (domain == "line.me" || strings::EndsWith(domain, ".line.me")) + else if (domain == kLineMe || strings::EndsWith(domain, kDotLineMe)) { auto webPath = url.GetPath(); if (strings::StartsWith(webPath, "R/ti/p/")) @@ -265,14 +304,15 @@ string ValidateAndFormat_contactLine(string const & linePage) // Parse https://line.me/R/home/public/main?id={LINE ID without @} // and https://line.me/R/home/public/profile?id={LINE ID without @} std::string const * id = url.GetParamValue("id"); - return (id? *id : std::string()); + return (id ? *id : std::string()); } else { - if (strings::StartsWith(linePage, "http://")) + if (strings::StartsWith(linePage, kHttp)) return linePage.substr(7); - if (strings::StartsWith(linePage, "https://")) + if (strings::StartsWith(linePage, kHttps)) return linePage.substr(8); + return linePage; } } @@ -289,7 +329,7 @@ bool ValidateWebsite(string const & site) if (startPos >= site.size()) return false; - // Site should contain at least one dot but not at the begining/end. + // Site should contain at least one dot but not at the beginning/end. if ('.' == site[startPos] || '.' == site.back()) return false; @@ -320,7 +360,8 @@ bool ValidateFacebookPage(string const & page) return false; string const domain = strings::MakeLowerCase(url::Url::FromString(page).GetHost()); - return (strings::StartsWith(domain, "facebook.") || strings::StartsWith(domain, "fb.") || + // Validate domain name: "facebook.*" or "fb.*" or "*.facebook.*" or "*.fb.*". + return (strings::StartsWith(domain, kFacebookDot) || strings::StartsWith(domain, kFbDot) || domain.find(".facebook.") != string::npos || domain.find(".fb.") != string::npos); } @@ -337,7 +378,7 @@ bool ValidateInstagramPage(string const & page) return false; string const domain = strings::MakeLowerCase(url::Url::FromString(page).GetHost()); - return domain == "instagram.com" || strings::EndsWith(domain, ".instagram.com"); + return domain == kInstagramCom || strings::EndsWith(domain, kDotInstagramCom); } bool ValidateTwitterPage(string const & page) @@ -349,7 +390,7 @@ bool ValidateTwitterPage(string const & page) return regex_match(page, s_twitterRegex); // Rules are defined here: https://stackoverflow.com/q/11361044 string const domain = strings::MakeLowerCase(url::Url::FromString(page).GetHost()); - return domain == "twitter.com" || strings::EndsWith(domain, ".twitter.com"); + return domain == kTwitterCom || strings::EndsWith(domain, kDotTwitterCom); } bool ValidateVkPage(string const & page) @@ -382,8 +423,8 @@ bool ValidateVkPage(string const & page) return false; string const domain = strings::MakeLowerCase(url::Url::FromString(page).GetHost()); - return domain == "vk.com" || strings::EndsWith(domain, ".vk.com") - || domain == "vkontakte.ru" || strings::EndsWith(domain, ".vkontakte.ru"); + return domain == kVkCom || strings::EndsWith(domain, kDotVkCom) + || domain == kVkontakteRu || strings::EndsWith(domain, kDotVkontakteRu); } bool ValidateLinePage(string const & page) @@ -406,7 +447,70 @@ bool ValidateLinePage(string const & page) string const domain = strings::MakeLowerCase(url::Url::FromString(page).GetHost()); // Check Line domain name. - return (domain == "line.me" || strings::EndsWith(domain, ".line.me")); + return (domain == kLineMe || strings::EndsWith(domain, kDotLineMe)); +} + +bool isSocialContactTag(string_view tag) +{ + return tag == kInstagram || tag == kFacebook || tag == kTwitter || tag == kVk || tag == kLine; +} + +bool isSocialContactTag(MapObject::MetadataID const metaID) +{ + return metaID == MapObject::MetadataID::FMD_CONTACT_INSTAGRAM || + metaID == MapObject::MetadataID::FMD_CONTACT_FACEBOOK || + metaID == MapObject::MetadataID::FMD_CONTACT_TWITTER || + metaID == MapObject::MetadataID::FMD_CONTACT_VK || + metaID == MapObject::MetadataID::FMD_CONTACT_LINE; +} + +// Functions ValidateAndFormat_{facebook,instagram,twitter,vk}(...) by default strip domain name +// from OSM data and user input. This function prepends domain name to generate full URL. +string socialContactToURL(string_view tag, string_view value) +{ + ASSERT(!value.empty(), ()); + + if (tag == kInstagram) + return string{kUrlInstagram}.append(value); + if (tag == kFacebook) + return string{kUrlFacebook}.append(value); + if (tag == kTwitter) + return string{kUrlTwitter}.append(value); + if (tag == kVk) + return string{kUrlVk}.append(value); + if (tag == kLine) + { + if (value.find('/') == string::npos) // 'value' is a username. + return string{kUrlLine}.append(value); + else // 'value' is an URL. + return string{kHttps}.append(value); + } + + return string{value}; +} + +string socialContactToURL(MapObject::MetadataID metaID, string_view value) +{ + ASSERT(!value.empty(), ()); + + switch (metaID) + { + case MapObject::MetadataID::FMD_CONTACT_INSTAGRAM: + return string{kUrlInstagram}.append(value); + case MapObject::MetadataID::FMD_CONTACT_FACEBOOK: + return string{kUrlFacebook}.append(value); + case MapObject::MetadataID::FMD_CONTACT_TWITTER: + return string{kUrlTwitter}.append(value); + case MapObject::MetadataID::FMD_CONTACT_VK: + return string{kUrlVk}.append(value); + case MapObject::MetadataID::FMD_CONTACT_LINE: + if (value.find('/') == string::npos) // 'value' is a username. + return string{kUrlLine}.append(value); + else // 'value' is an URL. + return string{kHttps}.append(value); + default: + return string{value}; + } } } // namespace osm diff --git a/indexer/validate_and_format_contacts.hpp b/indexer/validate_and_format_contacts.hpp index 2d2612b15b..7e430c1656 100644 --- a/indexer/validate_and_format_contacts.hpp +++ b/indexer/validate_and_format_contacts.hpp @@ -2,6 +2,8 @@ #include +#include "map_object.hpp" + namespace osm { std::string ValidateAndFormat_website(std::string const & v); @@ -17,4 +19,9 @@ bool ValidateInstagramPage(std::string const & v); bool ValidateTwitterPage(std::string const & v); bool ValidateVkPage(std::string const & v); bool ValidateLinePage(std::string const & v); -} + +bool isSocialContactTag(std::string_view tag); +bool isSocialContactTag(osm::MapObject::MetadataID const metaID); +std::string socialContactToURL(std::string_view tag, std::string_view value); +std::string socialContactToURL(osm::MapObject::MetadataID metaID, std::string_view value); +} // namespace osm diff --git a/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageInfoData.h b/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageInfoData.h index 9533f55927..a5da3f0f29 100644 --- a/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageInfoData.h +++ b/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageInfoData.h @@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, readonly, nullable) NSString *instagram; @property(nonatomic, readonly, nullable) NSString *twitter; @property(nonatomic, readonly, nullable) NSString *vk; +@property(nonatomic, readonly, nullable) NSString *line; @property(nonatomic, readonly, nullable) NSString *email; @property(nonatomic, readonly, nullable) NSURL *emailUrl; @property(nonatomic, readonly, nullable) NSString *cuisine; diff --git a/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageInfoData.mm b/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageInfoData.mm index 8e265d8ac1..22690d7f60 100644 --- a/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageInfoData.mm +++ b/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageInfoData.mm @@ -4,6 +4,8 @@ #import +#include "indexer/validate_and_format_contacts.hpp" + #include "map/place_page_info.hpp" using namespace place_page; @@ -56,6 +58,7 @@ using namespace osm; case MetadataID::FMD_CONTACT_INSTAGRAM: _instagram = ToNSString(value); break; case MetadataID::FMD_CONTACT_TWITTER: _twitter = ToNSString(value); break; case MetadataID::FMD_CONTACT_VK: _vk = ToNSString(value); break; + case MetadataID::FMD_CONTACT_LINE: _line = ToNSString(value); break; case MetadataID::FMD_OPERATOR: _ppOperator = ToNSString(value); break; case MetadataID::FMD_INTERNET: _wifiAvailable = (rawData.GetInternet() == osm::Internet::No) diff --git a/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/Contents.json b/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/Contents.json new file mode 100644 index 0000000000..62e2d1321d --- /dev/null +++ b/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "ic_placepage_line.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "ic_placepage_line@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ic_placepage_line@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/ic_placepage_line.png b/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/ic_placepage_line.png new file mode 100644 index 0000000000..f7d4ffcd78 Binary files /dev/null and b/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/ic_placepage_line.png differ diff --git a/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/ic_placepage_line@2x.png b/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/ic_placepage_line@2x.png new file mode 100644 index 0000000000..310d02bde9 Binary files /dev/null and b/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/ic_placepage_line@2x.png differ diff --git a/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/ic_placepage_line@3x.png b/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/ic_placepage_line@3x.png new file mode 100644 index 0000000000..d2cb054132 Binary files /dev/null and b/iphone/Maps/Images.xcassets/Place Page/ic_placepage_line.imageset/ic_placepage_line@3x.png differ diff --git a/iphone/Maps/UI/Editor/MWMEditorViewController.mm b/iphone/Maps/UI/Editor/MWMEditorViewController.mm index 60307afb8a..55a20bd0f0 100644 --- a/iphone/Maps/UI/Editor/MWMEditorViewController.mm +++ b/iphone/Maps/UI/Editor/MWMEditorViewController.mm @@ -26,6 +26,7 @@ #import #include "platform/localization.hpp" +#include "indexer/validate_and_format_contacts.hpp" namespace { @@ -416,10 +417,15 @@ void registerCellsForTableView(std::vector const & cells, UITab icon:(NSString * _Nonnull)icon placeholder:(NSString * _Nonnull)name { + MetadataID metaId = static_cast(cellID); + NSString* value = ToNSString(m_mapObject.GetMetadata(metaId)); + if (osm::isSocialContactTag(metaId) && [value containsString:@"/"]) + value = ToNSString(osm::socialContactToURL(metaId, [value UTF8String])); + MWMEditorTextTableViewCell * tCell = static_cast(cell); [tCell configWithDelegate:self icon:[UIImage imageNamed:icon] - text:ToNSString(m_mapObject.GetMetadata(static_cast(cellID))) + text:value placeholder:name keyboardType:UIKeyboardTypeDefault capitalization:UITextAutocapitalizationTypeSentences]; @@ -662,6 +668,14 @@ void registerCellsForTableView(std::vector const & cells, UITab placeholder:L(@"vk")]; break; } + case MetadataID::FMD_CONTACT_LINE: + { + [self configTextViewCell:cell + cellID:cellID + icon:@"ic_placepage_line" + placeholder:L(@"line")]; + break; + } case MWMEditorCellTypeNote: { MWMNoteCell * tCell = static_cast(cell); diff --git a/iphone/Maps/UI/PlacePage/Components/PlacePageInfoViewController.swift b/iphone/Maps/UI/PlacePage/Components/PlacePageInfoViewController.swift index 8f2428e6d7..a94cddb255 100644 --- a/iphone/Maps/UI/PlacePage/Components/PlacePageInfoViewController.swift +++ b/iphone/Maps/UI/PlacePage/Components/PlacePageInfoViewController.swift @@ -58,6 +58,7 @@ protocol PlacePageInfoViewControllerDelegate: AnyObject { func didPressInstagram() func didPressTwitter() func didPressVk() + func didPressLine() func didPressEmail() } @@ -84,6 +85,7 @@ class PlacePageInfoViewController: UIViewController { private var instagramView: InfoItemViewController? private var twitterView: InfoItemViewController? private var vkView: InfoItemViewController? + private var lineView: InfoItemViewController? private var cuisineView: InfoItemViewController? private var operatorView: InfoItemViewController? private var wifiView: InfoItemViewController? @@ -166,28 +168,34 @@ class PlacePageInfoViewController: UIViewController { } if let facebook = placePageInfoData.facebook { - facebookView = createInfoItem("@" + facebook, icon: UIImage(named: "ic_placepage_facebook"), style: .link) { [weak self] in + facebookView = createInfoItem(facebook, icon: UIImage(named: "ic_placepage_facebook"), style: .link) { [weak self] in self?.delegate?.didPressFacebook() } } if let instagram = placePageInfoData.instagram { - instagramView = createInfoItem("@" + instagram, icon: UIImage(named: "ic_placepage_instagram"), style: .link) { [weak self] in + instagramView = createInfoItem(instagram, icon: UIImage(named: "ic_placepage_instagram"), style: .link) { [weak self] in self?.delegate?.didPressInstagram() } } if let twitter = placePageInfoData.twitter { - twitterView = createInfoItem("@" + twitter, icon: UIImage(named: "ic_placepage_twitter"), style: .link) { [weak self] in + twitterView = createInfoItem(twitter, icon: UIImage(named: "ic_placepage_twitter"), style: .link) { [weak self] in self?.delegate?.didPressTwitter() } } if let vk = placePageInfoData.vk { - vkView = createInfoItem("@" + vk, icon: UIImage(named: "ic_placepage_vk"), style: .link) { [weak self] in + vkView = createInfoItem(vk, icon: UIImage(named: "ic_placepage_vk"), style: .link) { [weak self] in self?.delegate?.didPressVk() } } + + if let line = placePageInfoData.line { + lineView = createInfoItem(line, icon: UIImage(named: "ic_placepage_line"), style: .link) { [weak self] in + self?.delegate?.didPressLine() + } + } if let address = placePageInfoData.address { addressView = createInfoItem(address, icon: UIImage(named: "ic_placepage_adress")) diff --git a/iphone/Maps/UI/PlacePage/PlacePageInteractor.swift b/iphone/Maps/UI/PlacePage/PlacePageInteractor.swift index 5eec841fcb..95cd6a50fd 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageInteractor.swift +++ b/iphone/Maps/UI/PlacePage/PlacePageInteractor.swift @@ -57,6 +57,10 @@ extension PlacePageInteractor: PlacePageInfoViewControllerDelegate { MWMPlacePageManagerHelper.openVk(placePageData) } + func didPressLine() { + MWMPlacePageManagerHelper.openLine(placePageData) + } + func didPressEmail() { MWMPlacePageManagerHelper.openEmail(placePageData) } diff --git a/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManager.mm b/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManager.mm index d4d7b34603..a3f09197a7 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManager.mm +++ b/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManager.mm @@ -10,9 +10,12 @@ #import "location_util.h" #import +#import #include "platform/downloader_defines.hpp" +#include "indexer/validate_and_format_contacts.hpp" + using namespace storage; @interface MWMPlacePageManager () @@ -238,19 +241,28 @@ using namespace storage; } - (void)openFacebook:(PlacePageData *)data { - [self.ownerViewController openUrl:[NSString stringWithFormat:@"https://m.facebook.com/%@", data.infoData.facebook]]; + std::string const fullUrl = osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_FACEBOOK, [data.infoData.facebook UTF8String]); + [self.ownerViewController openUrl:ToNSString(fullUrl)]; } - (void)openInstagram:(PlacePageData *)data { - [self.ownerViewController openUrl:[NSString stringWithFormat:@"https://instagram.com/%@", data.infoData.instagram]]; + std::string const fullUrl = osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_INSTAGRAM, [data.infoData.instagram UTF8String]); + [self.ownerViewController openUrl:ToNSString(fullUrl)]; } - (void)openTwitter:(PlacePageData *)data { - [self.ownerViewController openUrl:[NSString stringWithFormat:@"https://mobile.twitter.com/%@", data.infoData.twitter]]; + std::string const fullUrl = osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_TWITTER, [data.infoData.twitter UTF8String]); + [self.ownerViewController openUrl:ToNSString(fullUrl)]; } - (void)openVk:(PlacePageData *)data { - [self.ownerViewController openUrl:[NSString stringWithFormat:@"https://vk.com/%@", data.infoData.vk]]; + std::string const fullUrl = osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_VK, [data.infoData.vk UTF8String]); + [self.ownerViewController openUrl:ToNSString(fullUrl)]; +} + +- (void)openLine:(PlacePageData *)data { + std::string const fullUrl = osm::socialContactToURL(osm::MapObject::MetadataID::FMD_CONTACT_LINE, [data.infoData.line UTF8String]); + [self.ownerViewController openUrl:ToNSString(fullUrl)]; } - (void)openEmail:(PlacePageData *)data { diff --git a/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManagerHelper.h b/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManagerHelper.h index 669eea8ca2..13fffc6b67 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManagerHelper.h +++ b/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManagerHelper.h @@ -15,6 +15,7 @@ + (void)openInstagram:(PlacePageData *)data; + (void)openTwitter:(PlacePageData *)data; + (void)openVk:(PlacePageData *)data; ++ (void)openLine:(PlacePageData *)data; + (void)call:(PlacePageData *)data; + (void)showAllFacilities:(PlacePageData *)data; + (void)showPlaceDescription:(NSString *)htmlString; diff --git a/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManagerHelper.mm b/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManagerHelper.mm index 099d1272eb..6fe4622cda 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManagerHelper.mm +++ b/iphone/Maps/UI/PlacePage/PlacePageManager/MWMPlacePageManagerHelper.mm @@ -22,6 +22,7 @@ - (void)openInstagram:(PlacePageData *)data; - (void)openTwitter:(PlacePageData *)data; - (void)openVk:(PlacePageData *)data; +- (void)openLine:(PlacePageData *)data; - (void)call:(PlacePageData *)data; - (void)showAllFacilities:(PlacePageData *)data; - (void)showPlaceDescription:(NSString *)htmlString; @@ -98,6 +99,10 @@ [[MWMMapViewControlsManager manager].placePageManager openVk:data]; } ++ (void)openLine:(PlacePageData *)data { + [[MWMMapViewControlsManager manager].placePageManager openLine:data]; +} + + (void)call:(PlacePageData *)data { [[MWMMapViewControlsManager manager].placePageManager call:data]; }