From da7362e943ebf0543ad464cfb27a86e541f186db Mon Sep 17 00:00:00 2001 From: Ilya Grechuhin Date: Mon, 24 Jul 2017 17:01:28 +0300 Subject: [PATCH] [MAPSME-4971] [ios] Added Cian support to place page. --- iphone/Maps/Maps.xcodeproj/project.pbxproj | 48 +++++ iphone/Maps/UI/PlacePage/MWMPlacePageData.h | 12 +- iphone/Maps/UI/PlacePage/MWMPlacePageData.mm | 59 +++++- .../Maps/UI/PlacePage/MWMPlacePageManager.mm | 8 +- .../Content/Cian/CianElement.swift | 174 ++++++++++++++++++ .../Content/Cian/CianElement.xib | 167 +++++++++++++++++ .../Content/Cian/CianItemModel.swift | 14 ++ .../Content/Cian/PPCianCarouselCell.swift | 106 +++++++++++ .../Content/Cian/PPCianCarouselCell.xib | 74 ++++++++ .../PlacePageLayout/MWMPlacePageLayout.mm | 34 +++- 10 files changed, 686 insertions(+), 10 deletions(-) create mode 100644 iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianElement.swift create mode 100644 iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianElement.xib create mode 100644 iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianItemModel.swift create mode 100644 iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/PPCianCarouselCell.swift create mode 100644 iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/PPCianCarouselCell.xib diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index 5a7fccae5e..7fd8a382fc 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -275,6 +275,21 @@ 3476B8E01BFDD33A00874594 /* tts-how-to-set-up-voice-img in Resources */ = {isa = PBXBuildFile; fileRef = 3476B8DF1BFDD33A00874594 /* tts-how-to-set-up-voice-img */; }; 3476B8E11BFDD33A00874594 /* tts-how-to-set-up-voice-img in Resources */ = {isa = PBXBuildFile; fileRef = 3476B8DF1BFDD33A00874594 /* tts-how-to-set-up-voice-img */; }; 347A4C5E1C4E76C9006BA66E /* liboauthcpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 340DC82B1C4E72C700EAA2CC /* liboauthcpp.a */; }; + 347E1A881F1F5DD7002BF7A8 /* CianItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E1A871F1F5DD7002BF7A8 /* CianItemModel.swift */; }; + 347E1A891F1F5DD7002BF7A8 /* CianItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E1A871F1F5DD7002BF7A8 /* CianItemModel.swift */; }; + 347E1A8A1F1F5DD7002BF7A8 /* CianItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E1A871F1F5DD7002BF7A8 /* CianItemModel.swift */; }; + 347E1A8D1F1F71F1002BF7A8 /* PPCianCarouselCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E1A8C1F1F71F1002BF7A8 /* PPCianCarouselCell.swift */; }; + 347E1A8E1F1F71F1002BF7A8 /* PPCianCarouselCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E1A8C1F1F71F1002BF7A8 /* PPCianCarouselCell.swift */; }; + 347E1A8F1F1F71F1002BF7A8 /* PPCianCarouselCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E1A8C1F1F71F1002BF7A8 /* PPCianCarouselCell.swift */; }; + 347E1A911F1F72AD002BF7A8 /* PPCianCarouselCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 347E1A901F1F72AD002BF7A8 /* PPCianCarouselCell.xib */; }; + 347E1A921F1F72AD002BF7A8 /* PPCianCarouselCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 347E1A901F1F72AD002BF7A8 /* PPCianCarouselCell.xib */; }; + 347E1A931F1F72AD002BF7A8 /* PPCianCarouselCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 347E1A901F1F72AD002BF7A8 /* PPCianCarouselCell.xib */; }; + 347E1A961F1F7404002BF7A8 /* CianElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E1A941F1F7404002BF7A8 /* CianElement.swift */; }; + 347E1A971F1F7404002BF7A8 /* CianElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E1A941F1F7404002BF7A8 /* CianElement.swift */; }; + 347E1A981F1F7404002BF7A8 /* CianElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E1A941F1F7404002BF7A8 /* CianElement.swift */; }; + 347E1A991F1F7404002BF7A8 /* CianElement.xib in Resources */ = {isa = PBXBuildFile; fileRef = 347E1A951F1F7404002BF7A8 /* CianElement.xib */; }; + 347E1A9A1F1F7404002BF7A8 /* CianElement.xib in Resources */ = {isa = PBXBuildFile; fileRef = 347E1A951F1F7404002BF7A8 /* CianElement.xib */; }; + 347E1A9B1F1F7404002BF7A8 /* CianElement.xib in Resources */ = {isa = PBXBuildFile; fileRef = 347E1A951F1F7404002BF7A8 /* CianElement.xib */; }; 34845DAE1E1649F6003D55B9 /* DownloaderNoResultsEmbedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34845DAD1E1649F6003D55B9 /* DownloaderNoResultsEmbedViewController.swift */; }; 34845DAF1E1649F6003D55B9 /* DownloaderNoResultsEmbedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34845DAD1E1649F6003D55B9 /* DownloaderNoResultsEmbedViewController.swift */; }; 34845DB01E1649F6003D55B9 /* DownloaderNoResultsEmbedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34845DAD1E1649F6003D55B9 /* DownloaderNoResultsEmbedViewController.swift */; }; @@ -1811,6 +1826,11 @@ 347526FB1DC0B00F00918CF5 /* common-release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "common-release.xcconfig"; path = "../../xcode/common-release.xcconfig"; sourceTree = ""; }; 3476B8D51BFDD30B00874594 /* tts-how-to-set-up-voice.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = "tts-how-to-set-up-voice.html"; path = "../../data/tts-how-to-set-up-voice.html"; sourceTree = ""; }; 3476B8DF1BFDD33A00874594 /* tts-how-to-set-up-voice-img */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "tts-how-to-set-up-voice-img"; path = "../../data/tts-how-to-set-up-voice-img"; sourceTree = ""; }; + 347E1A871F1F5DD7002BF7A8 /* CianItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CianItemModel.swift; sourceTree = ""; }; + 347E1A8C1F1F71F1002BF7A8 /* PPCianCarouselCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PPCianCarouselCell.swift; sourceTree = ""; }; + 347E1A901F1F72AD002BF7A8 /* PPCianCarouselCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PPCianCarouselCell.xib; sourceTree = ""; }; + 347E1A941F1F7404002BF7A8 /* CianElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CianElement.swift; sourceTree = ""; }; + 347E1A951F1F7404002BF7A8 /* CianElement.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CianElement.xib; sourceTree = ""; }; 348320CC1B6A2C52007EC039 /* MWMNavigationViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMNavigationViewProtocol.h; sourceTree = ""; }; 34845DAD1E1649F6003D55B9 /* DownloaderNoResultsEmbedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloaderNoResultsEmbedViewController.swift; sourceTree = ""; }; 34845DB11E165E24003D55B9 /* SearchNoResultsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchNoResultsViewController.swift; sourceTree = ""; }; @@ -3199,6 +3219,18 @@ path = Components; sourceTree = ""; }; + 347E1A861F1F5DD7002BF7A8 /* Cian */ = { + isa = PBXGroup; + children = ( + 347E1A871F1F5DD7002BF7A8 /* CianItemModel.swift */, + 347E1A8C1F1F71F1002BF7A8 /* PPCianCarouselCell.swift */, + 347E1A901F1F72AD002BF7A8 /* PPCianCarouselCell.xib */, + 347E1A941F1F7404002BF7A8 /* CianElement.swift */, + 347E1A951F1F7404002BF7A8 /* CianElement.xib */, + ); + path = Cian; + sourceTree = ""; + }; 3486B5041E27A4B50069C126 /* Notifications */ = { isa = PBXGroup; children = ( @@ -4074,6 +4106,7 @@ F6E2FCA81E097B9F0083EBEC /* Content */ = { isa = PBXGroup; children = ( + 347E1A861F1F5DD7002BF7A8 /* Cian */, 34EE259B1EFA681000F870AB /* ViatorCells */, F68BDF181EF80DCC0009BB81 /* UGC */, 346DB81C1E5C4F6700E3123E /* Gallery */, @@ -4761,6 +4794,7 @@ F6E2FF201E097BA00083EBEC /* MWMSearchTabbedViewController.xib in Resources */, F6E2FF291E097BA00083EBEC /* MWMSearchTabButtonsView.xib in Resources */, F6E2FF411E097BA00083EBEC /* MWMSearchTableViewController.xib in Resources */, + 347E1A911F1F72AD002BF7A8 /* PPCianCarouselCell.xib in Resources */, F6E2FEED1E097BA00083EBEC /* MWMSearchView.xib in Resources */, 3490D2E21CE9DD2500D0B838 /* MWMSideButtonsView.xib in Resources */, 346DB82A1E5C4F6700E3123E /* GalleryCell.xib in Resources */, @@ -4785,6 +4819,7 @@ 4A23D15F1B8B4DD700D4EB6F /* resources-xxhdpi_clear in Resources */, 4A7D89C81B2EBF3B00AC843E /* resources-xxhdpi_dark in Resources */, 340E1EF41E2F614400CE49BF /* SearchFilters.storyboard in Resources */, + 347E1A991F1F7404002BF7A8 /* CianElement.xib in Resources */, F682249E1E5B105900BC1C18 /* PPHotelDescriptionCell.xib in Resources */, 340E1EF71E2F614400CE49BF /* Settings.storyboard in Resources */, 5605022F1B6211E100169CAD /* sound-strings in Resources */, @@ -4939,6 +4974,7 @@ F6E2FF421E097BA00083EBEC /* MWMSearchTableViewController.xib in Resources */, F6E2FEEE1E097BA00083EBEC /* MWMSearchView.xib in Resources */, 3490D2E31CE9DD2500D0B838 /* MWMSideButtonsView.xib in Resources */, + 347E1A921F1F72AD002BF7A8 /* PPCianCarouselCell.xib in Resources */, 346DB82B1E5C4F6700E3123E /* GalleryCell.xib in Resources */, F6E2FE2E1E097BA00083EBEC /* MWMStreetEditorEditTableViewCell.xib in Resources */, F68FCB8D1DA7BD20007CC7D7 /* RoutePreviewTaxiCell.xib in Resources */, @@ -4963,6 +4999,7 @@ 6741A94D1BF340DE002C974C /* resources-xxhdpi_clear in Resources */, 6741A9551BF340DE002C974C /* resources-xxhdpi_dark in Resources */, 340E1EF51E2F614400CE49BF /* SearchFilters.storyboard in Resources */, + 347E1A9A1F1F7404002BF7A8 /* CianElement.xib in Resources */, F682249F1E5B105900BC1C18 /* PPHotelDescriptionCell.xib in Resources */, 340E1EF81E2F614400CE49BF /* Settings.storyboard in Resources */, 6741A9421BF340DE002C974C /* sound-strings in Resources */, @@ -5117,6 +5154,7 @@ F6E2FF221E097BA00083EBEC /* MWMSearchTabbedViewController.xib in Resources */, F6E2FF2B1E097BA00083EBEC /* MWMSearchTabButtonsView.xib in Resources */, F6E2FF431E097BA00083EBEC /* MWMSearchTableViewController.xib in Resources */, + 347E1A931F1F72AD002BF7A8 /* PPCianCarouselCell.xib in Resources */, F6E2FEEF1E097BA00083EBEC /* MWMSearchView.xib in Resources */, 849CF6471DE842290024A8A5 /* MWMSideButtonsView.xib in Resources */, 346DB82C1E5C4F6700E3123E /* GalleryCell.xib in Resources */, @@ -5141,6 +5179,7 @@ 849CF61A1DE842290024A8A5 /* resources-xhdpi_dark in Resources */, 849CF5FC1DE842290024A8A5 /* resources-xxhdpi_clear in Resources */, 849CF6061DE842290024A8A5 /* resources-xxhdpi_dark in Resources */, + 347E1A9B1F1F7404002BF7A8 /* CianElement.xib in Resources */, 340E1EF61E2F614400CE49BF /* SearchFilters.storyboard in Resources */, 340E1EF91E2F614400CE49BF /* Settings.storyboard in Resources */, 849CF5EA1DE842290024A8A5 /* sound-strings in Resources */, @@ -5296,6 +5335,7 @@ F6E2FF5C1E097BA00083EBEC /* MWMRecentTrackSettingsController.mm in Sources */, 343064401E9FDC7300DC7665 /* SearchIndex.swift in Sources */, F6664BF91E6459CB00E703C2 /* PPFacilityCell.swift in Sources */, + 347E1A8D1F1F71F1002BF7A8 /* PPCianCarouselCell.swift in Sources */, F6E2FDE81E097BA00083EBEC /* MWMObjectsCategorySelectorController.mm in Sources */, F64F199D1AB81A00006EAF7E /* MWMDefaultAlert.mm in Sources */, F6150E431EFA52E9000B955D /* UGCSpecificReviewCell.swift in Sources */, @@ -5322,6 +5362,7 @@ 34F4073D1E9E1AFF00E57AC0 /* MPNativeAd+MWM.mm in Sources */, F69018B81E9E601400B3C10B /* MWMAutoupdateController.mm in Sources */, 560634F21B78806100F3D670 /* MWMTextToSpeech.mm in Sources */, + 347E1A961F1F7404002BF7A8 /* CianElement.swift in Sources */, F6E2FECF1E097BA00083EBEC /* MWMSearchFilterViewController.mm in Sources */, 34D4FA661E265749003F53EF /* WhatsNewController.swift in Sources */, 34D3B01A1E389D05004100F9 /* MWMButtonCell.mm in Sources */, @@ -5389,6 +5430,7 @@ 34943BB61E26222300B14F84 /* WelcomeProtocol.swift in Sources */, F6E2FD5E1E097BA00083EBEC /* MWMMapDownloaderLargeCountryTableViewCell.mm in Sources */, F6558DA11E642CC0002203AE /* MWMFacilitiesController.mm in Sources */, + 347E1A881F1F5DD7002BF7A8 /* CianItemModel.swift in Sources */, F6E2FF471E097BA00083EBEC /* SettingsTableViewSelectableCell.swift in Sources */, 349D1ACE1E2E325B004A2006 /* MWMBottomMenuCollectionViewCell.mm in Sources */, F6E2FF261E097BA00083EBEC /* MWMSearchTabButtonsView.mm in Sources */, @@ -5610,6 +5652,7 @@ F6E2FF5D1E097BA00083EBEC /* MWMRecentTrackSettingsController.mm in Sources */, 343064411E9FDC7300DC7665 /* SearchIndex.swift in Sources */, F6664BFA1E6459CB00E703C2 /* PPFacilityCell.swift in Sources */, + 347E1A8E1F1F71F1002BF7A8 /* PPCianCarouselCell.swift in Sources */, F6E2FDE91E097BA00083EBEC /* MWMObjectsCategorySelectorController.mm in Sources */, 6741A9A81BF340DE002C974C /* MWMFacebookAlert.mm in Sources */, F6150E441EFA52E9000B955D /* UGCSpecificReviewCell.swift in Sources */, @@ -5636,6 +5679,7 @@ 34F4073E1E9E1AFF00E57AC0 /* MPNativeAd+MWM.mm in Sources */, 6741A9B61BF340DE002C974C /* MWMTextToSpeech.mm in Sources */, F6E2FED01E097BA00083EBEC /* MWMSearchFilterViewController.mm in Sources */, + 347E1A971F1F7404002BF7A8 /* CianElement.swift in Sources */, 34D4FA671E265749003F53EF /* WhatsNewController.swift in Sources */, 34D3B01B1E389D05004100F9 /* MWMButtonCell.mm in Sources */, 3486B5161E27AD3B0069C126 /* Framework.cpp in Sources */, @@ -5703,6 +5747,7 @@ F6E2FF271E097BA00083EBEC /* MWMSearchTabButtonsView.mm in Sources */, F6558DA21E642CC0002203AE /* MWMFacilitiesController.mm in Sources */, 349D1ACF1E2E325B004A2006 /* MWMBottomMenuCollectionViewCell.mm in Sources */, + 347E1A891F1F5DD7002BF7A8 /* CianItemModel.swift in Sources */, F6E2FF451E097BA00083EBEC /* SettingsTableViewLinkCell.swift in Sources */, 34C9BD0A1C6DBCDA000DC38D /* MWMNavigationController.mm in Sources */, F6E2FE311E097BA00083EBEC /* MWMStreetEditorViewController.mm in Sources */, @@ -5924,6 +5969,7 @@ F6E2FF5E1E097BA00083EBEC /* MWMRecentTrackSettingsController.mm in Sources */, 343064421E9FDC7300DC7665 /* SearchIndex.swift in Sources */, F6664BFB1E6459CB00E703C2 /* PPFacilityCell.swift in Sources */, + 347E1A8F1F1F71F1002BF7A8 /* PPCianCarouselCell.swift in Sources */, F6E2FDEA1E097BA00083EBEC /* MWMObjectsCategorySelectorController.mm in Sources */, 849CF68A1DE842290024A8A5 /* MWMDefaultAlert.mm in Sources */, F6150E451EFA52E9000B955D /* UGCSpecificReviewCell.swift in Sources */, @@ -5950,6 +5996,7 @@ 34F4073F1E9E1AFF00E57AC0 /* MPNativeAd+MWM.mm in Sources */, 849CF69D1DE842290024A8A5 /* MWMInputLoginValidator.mm in Sources */, F6E2FED11E097BA00083EBEC /* MWMSearchFilterViewController.mm in Sources */, + 347E1A981F1F7404002BF7A8 /* CianElement.swift in Sources */, 34D4FA681E265749003F53EF /* WhatsNewController.swift in Sources */, 34D3B01C1E389D05004100F9 /* MWMButtonCell.mm in Sources */, 3486B5171E27AD3B0069C126 /* Framework.cpp in Sources */, @@ -6017,6 +6064,7 @@ 34943BB81E26222300B14F84 /* WelcomeProtocol.swift in Sources */, F6E2FD601E097BA00083EBEC /* MWMMapDownloaderLargeCountryTableViewCell.mm in Sources */, F6558DA31E642CC0002203AE /* MWMFacilitiesController.mm in Sources */, + 347E1A8A1F1F5DD7002BF7A8 /* CianItemModel.swift in Sources */, F6E2FF491E097BA00083EBEC /* SettingsTableViewSelectableCell.swift in Sources */, 349D1AD01E2E325B004A2006 /* MWMBottomMenuCollectionViewCell.mm in Sources */, F6E2FF281E097BA00083EBEC /* MWMSearchTabButtonsView.mm in Sources */, diff --git a/iphone/Maps/UI/PlacePage/MWMPlacePageData.h b/iphone/Maps/UI/PlacePage/MWMPlacePageData.h index 60cd8d07a1..d298d718e6 100644 --- a/iphone/Maps/UI/PlacePage/MWMPlacePageData.h +++ b/iphone/Maps/UI/PlacePage/MWMPlacePageData.h @@ -10,6 +10,7 @@ @class MWMPlacePageData; @class MWMUGCReviewVM; +@class MWMCianItemModel; namespace ugc { @@ -72,6 +73,7 @@ enum class HotelReviewsRow enum class SpecialProject { Viator, + Cian }; enum class MetainfoRows @@ -120,20 +122,22 @@ enum class OpeningHours }; using NewSectionsAreReady = void (^)(NSRange const & range, MWMPlacePageData * data, BOOL isSection); -using BannerIsReady = void (^)(); +using CianIsReady = void (^)(NSArray * items); } // namespace place_page @class MWMGalleryItemModel; @class MWMViatorItemModel; +@class MWMCianItemModel; @protocol MWMBanner; /// ViewModel for place page. @interface MWMPlacePageData : NSObject @property(copy, nonatomic) place_page::NewSectionsAreReady sectionsAreReadyCallback; -@property(copy, nonatomic) place_page::BannerIsReady bannerIsReadyCallback; +@property(copy, nonatomic) MWMVoidBlock bannerIsReadyCallback; +@property(copy, nonatomic) place_page::CianIsReady cianIsReadyCallback; // ready callback will be called from main queue. - (instancetype)initWithPlacePageInfo:(place_page::Info const &)info; @@ -172,6 +176,9 @@ using BannerIsReady = void (^)(); - (void)fillOnlineViatorSection; - (NSArray *)viatorItems; +// CIAN +- (void)fillOnlineCianSection; + // UGC - (MWMUGCReviewVM *)reviewViewModel; - (std::vector const &)ugcReviews; @@ -223,6 +230,7 @@ using BannerIsReady = void (^)(); - (BOOL)isBooking; - (BOOL)isOpentable; - (BOOL)isViator; +- (BOOL)isCian; - (BOOL)isBookingSearch; - (BOOL)isHTMLDescription; - (BOOL)isMyPosition; diff --git a/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm b/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm index 7bf98d390d..cd96696638 100644 --- a/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm +++ b/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm @@ -57,6 +57,8 @@ using namespace place_page; std::vector m_ugcRows; booking::HotelInfo m_hotelInfo; + + uint64_t m_cianRequestId; } - (instancetype)initWithPlacePageInfo:(Info const &)info @@ -135,8 +137,9 @@ using namespace place_page; NSAssert(!m_previewRows.empty(), @"Preview row's can't be empty!"); m_previewRows.push_back(PreviewRows::Space); + if (network_policy::CanUseNetwork() && ![MWMSettings adForbidden] && m_info.HasBanner() && - ![self isViator]) + ![self isViator] && ![self isCian]) { __weak auto wSelf = self; [[MWMBannersCache cache] @@ -284,6 +287,59 @@ using namespace place_page; }); } +- (void)fillOnlineCianSection +{ + if (![self isCian]) + return; + + [self insertSpecialProjectsSectionWithProject:SpecialProject::Cian]; + + if (Platform::ConnectionStatus() == Platform::EConnectionType::CONNECTION_NONE) + return; + + network_policy::CallPartnersApi([self](platform::NetworkPolicy const & canUseNetwork) { + auto api = GetFramework().GetCianApi(canUseNetwork); + if (!api) + return; + m_cianRequestId = api->GetRentNearby([self latLon], + [self](std::vector const & places, uint64_t const requestId) + { + if (self->m_cianRequestId != requestId) + return; + NSMutableArray * items = [@[] mutableCopy]; + for (auto const & p : places) + { + auto const & offers = p.m_offers; + NSCAssert(!offers.empty(), @"Cian misses offers for place."); + if (offers.empty()) + continue; + auto const & firstOffer = offers.front(); + + auto pageURL = [NSURL URLWithString:@(firstOffer.m_url.c_str())]; + if (!pageURL) + continue; + auto item = [[MWMCianItemModel alloc] initWithRoomsCount:firstOffer.m_roomsCount + priceRur:firstOffer.m_priceRur + pageURL:pageURL + address:@(firstOffer.m_address.c_str())]; + [items addObject:item]; + } + + dispatch_async(dispatch_get_main_queue(), [items, self] { + self.cianIsReadyCallback(items); + }); + }, + [self](int code, uint64_t const requestId) + { + if (self->m_cianRequestId != requestId) + return; + dispatch_async(dispatch_get_main_queue(), [self] { + self.cianIsReadyCallback(@[]); + }); + }); + }); +} + - (void)fillOnlineBookingSections { if (!self.isBooking) @@ -716,6 +772,7 @@ using namespace place_page; - (BOOL)isBooking { return m_info.GetSponsoredType() == SponsoredType::Booking; } - (BOOL)isOpentable { return m_info.GetSponsoredType() == SponsoredType::Opentable; } - (BOOL)isViator { return m_info.GetSponsoredType() == SponsoredType::Viator; } +- (BOOL)isCian { return m_info.GetSponsoredType() == SponsoredType::Cian; } - (BOOL)isBookingSearch { return !m_info.GetBookingSearchUrl().empty(); } - (BOOL)isMyPosition { return m_info.IsMyPosition(); } - (BOOL)isHTMLDescription { return strings::IsHTML(m_info.GetBookmarkData().GetDescription()); } diff --git a/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm b/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm index cd5d0b13b6..07dda82d61 100644 --- a/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm +++ b/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm @@ -264,7 +264,13 @@ void logPointEvent(MWMRoutePoint * pt, NSString * eventType) - (void)shouldDestroyLayout { self.layout = nil; } - (void)shouldClose { GetFramework().DeactivateMapSelection(true); } -- (BOOL)isExpandedOnShow { return self.data.isViator; } + +- (BOOL)isExpandedOnShow +{ + auto data = self.data; + return data.isViator || data.isCian; +} + - (void)onExpanded { if (self.isSponsoredOpenLogged) diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianElement.swift b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianElement.swift new file mode 100644 index 0000000000..cfc1325365 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianElement.swift @@ -0,0 +1,174 @@ +final class CianElement : UICollectionViewCell { + enum State { + case pending(onButtonAction: () -> Void) + case offer(model: CianItemModel?, onButtonAction: (CianItemModel?) -> Void) + case error(onButtonAction: () -> Void) + } + + @IBOutlet private var contentViews: [UIView]! + + @IBOutlet private weak var pendingView: UIView! + @IBOutlet private weak var offerView: UIView! + @IBOutlet private weak var more: UIButton! + @IBOutlet private weak var price: UILabel! { + didSet { + price.font = UIFont.medium14() + price.textColor = UIColor.linkBlue() + } + } + @IBOutlet private weak var descr: UILabel! { + didSet { + descr.font = UIFont.medium14() + descr.textColor = UIColor.blackPrimaryText() + } + } + @IBOutlet private weak var address: UILabel! { + didSet { + address.font = UIFont.regular12() + address.textColor = UIColor.blackSecondaryText() + } + } + @IBOutlet private weak var details: UIButton! { + didSet { + details.setTitleColor(UIColor.linkBlue(), for: .normal) + details.setBackgroundColor(UIColor.blackOpaque(), for: .normal) + } + } + + private var isLastCell = false { + didSet { + more.isHidden = !isLastCell + price.isHidden = isLastCell + descr.isHidden = isLastCell + address.isHidden = isLastCell + details.isHidden = isLastCell + } + } + + @IBOutlet private weak var pendingSpinnerView: UIImageView! { + didSet { + pendingSpinnerView.tintColor = UIColor.linkBlue() + } + } + @IBOutlet private weak var pendingTitleTopOffset: NSLayoutConstraint! + @IBOutlet private weak var pendingTitle: UILabel! { + didSet { + pendingTitle.font = UIFont.medium14() + pendingTitle.textColor = UIColor.blackPrimaryText() + pendingTitle.text = L("preloader_cian_title") + } + } + @IBOutlet private weak var pendingDescription: UILabel! { + didSet { + pendingDescription.font = UIFont.regular12() + pendingDescription.textColor = UIColor.blackSecondaryText() + pendingDescription.text = L("preloader_cian_message") + } + } + + + @IBAction func onButtonAction() { + switch state! { + case let .pending(action): action() + case let .offer(model, action): action(model) + case let .error(action): action() + } + } + + + var state: State! { + didSet { + setupAppearance() + setNeedsLayout() + contentViews.forEach { $0.isHidden = false } + let visibleView: UIView + var pendingSpinnerViewAlpha: CGFloat = 1 + switch state! { + case .pending: + visibleView = pendingView + configPending() + case let .offer(model, _): + visibleView = offerView + configOffer(model: model) + case .error: + pendingSpinnerViewAlpha = 0 + visibleView = pendingView + configError() + } + UIView.animate(withDuration: kDefaultAnimationDuration, + animations: { + self.pendingSpinnerView.alpha = pendingSpinnerViewAlpha + self.contentViews.forEach { $0.alpha = 0 } + visibleView.alpha = 1 + self.layoutIfNeeded() + }, completion: { _ in + self.contentViews.forEach { $0.isHidden = true } + visibleView.isHidden = false + }) + } + } + + private var isSpinning = false { + didSet { + let animationKey = "SpinnerAnimation" + if isSpinning { + let animation = CABasicAnimation(keyPath: "transform.rotation.z") + animation.fromValue = NSNumber(value: 0) + animation.toValue = NSNumber(value: 2 * Double.pi) + animation.duration = 0.8 + animation.repeatCount = Float.infinity + pendingSpinnerView.layer.add(animation, forKey: animationKey) + } else { + pendingSpinnerView.layer.removeAnimation(forKey: animationKey) + } + } + } + + private func setupAppearance() { + backgroundColor = UIColor.white() + layer.cornerRadius = 6 + layer.borderWidth = 1 + layer.borderColor = UIColor.blackDividers().cgColor + } + + private func configPending() { + isSpinning = true + details.setTitle(L("preloader_cian_button"), for: .normal) + pendingTitleTopOffset.priority = UILayoutPriorityDefaultLow + } + + private func configError() { + isSpinning = false + details.setTitle(L("preloader_cian_button"), for: .normal) + pendingTitleTopOffset.priority = UILayoutPriorityDefaultHigh + } + + private func configOffer(model: CianItemModel?) { + isSpinning = false + if let model = model { + isLastCell = false + + let priceFormatter = NumberFormatter() + priceFormatter.usesGroupingSeparator = true + if let priceString = priceFormatter.string(from: NSNumber(value: model.priceRur)) { + price.text = "\(priceString) \(L("rub_month"))" + } else { + price.isHidden = true + } + + let descrFormat = L("room").replacingOccurrences(of: "%s", with: "%@") + descr.text = String(format: descrFormat, arguments:["\(model.roomsCount)"]) + + address.text = model.address + + details.setTitle(L("details"), for: .normal) + } else { + isLastCell = true + + more.setBackgroundImage(UIColor.isNightMode() ? #imageLiteral(resourceName: "btn_float_more_dark") : #imageLiteral(resourceName: "btn_float_more_light"), for: .normal) + + backgroundColor = UIColor.clear + layer.borderColor = UIColor.clear.cgColor + } + } +} diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianElement.xib b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianElement.xib new file mode 100644 index 0000000000..83c4ba1688 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianElement.xib @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianItemModel.swift b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianItemModel.swift new file mode 100644 index 0000000000..67e47211a8 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/CianItemModel.swift @@ -0,0 +1,14 @@ +@objc(MWMCianItemModel) +final class CianItemModel: NSObject { + let roomsCount: UInt + let priceRur: UInt + let pageURL: URL + let address: String + + init(roomsCount: UInt, priceRur: UInt, pageURL: URL, address: String) { + self.roomsCount = roomsCount + self.priceRur = priceRur + self.pageURL = pageURL + self.address = address + } +} diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/PPCianCarouselCell.swift b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/PPCianCarouselCell.swift new file mode 100644 index 0000000000..590a7c3220 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/PPCianCarouselCell.swift @@ -0,0 +1,106 @@ +@objc(MWMPPCianCarouselCell) +final class PPCianCarouselCell: MWMTableViewCell { + @IBOutlet private weak var title: UILabel! { + didSet { + title.text = L("subtitle_rent") + title.font = UIFont.bold14() + title.textColor = UIColor.blackSecondaryText() + } + } + @IBOutlet private weak var more: UIButton! { + didSet { + more.setImage(#imageLiteral(resourceName: "logo_cian"), for: .normal) + more.titleLabel?.font = UIFont.regular17() + more.setTitleColor(UIColor.linkBlue(), for: .normal) + } + } + + @IBOutlet private weak var collectionView: UICollectionView! + var data: [CianItemModel]? { + didSet { + updateCollectionView { [weak self] in + self?.collectionView.reloadSections(IndexSet(integer: 0)) + } + } + } + fileprivate let kMaximumNumberOfElements = 5 + fileprivate var delegate: MWMPlacePageButtonsProtocol? + + func config(delegate d: MWMPlacePageButtonsProtocol?) { + delegate = d + collectionView.contentOffset = .zero + collectionView.delegate = self + collectionView.dataSource = self + collectionView.register(cellClass: CianElement.self) + collectionView.reloadData() + + isSeparatorHidden = true + backgroundColor = UIColor.clear + } + + fileprivate func isLastCell(_ indexPath: IndexPath) -> Bool { + return indexPath.item == collectionView.numberOfItems(inSection: indexPath.section) - 1 + } + + override func didMoveToSuperview() { + super.didMoveToSuperview() + updateCollectionView(nil) + } + + private func updateCollectionView(_ updates: (() -> Void)?) { + guard let sv = superview else { return } + let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout + let screenSize = { sv.size.width - layout.sectionInset.left - layout.sectionInset.right } + let itemHeight: CGFloat = 136 + let itemWidth: CGFloat + if let data = data { + if data.isEmpty { + itemWidth = screenSize() + } else { + itemWidth = 160 + } + } else { + itemWidth = screenSize() + } + layout.itemSize = CGSize(width: itemWidth, height: itemHeight) + collectionView.performBatchUpdates(updates, completion: nil) + } + + @IBAction + fileprivate func onMore() { + delegate?.openSponsoredURL(nil) + } +} + +extension PPCianCarouselCell: UICollectionViewDelegate, UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withCellClass: CianElement.self, + indexPath: indexPath) as! CianElement + if let data = data { + if data.isEmpty { + cell.state = .error(onButtonAction: onMore) + } else { + let model = isLastCell(indexPath) ? nil : data[indexPath.item] + cell.state = .offer(model: model, + onButtonAction: { [unowned self] model in + self.delegate?.openSponsoredURL(model?.pageURL) + }) + } + } else { + cell.state = .pending(onButtonAction: onMore) + } + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + if let data = data { + if data.isEmpty { + return 1 + } else { + return min(data.count, kMaximumNumberOfElements) + 1 + } + } else { + return 1 + } + } +} diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/PPCianCarouselCell.xib b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/PPCianCarouselCell.xib new file mode 100644 index 0000000000..6caaa12e06 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/Cian/PPCianCarouselCell.xib @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/MWMPlacePageLayout.mm b/iphone/Maps/UI/PlacePage/PlacePageLayout/MWMPlacePageLayout.mm index c1e01ac79d..7cedd05eba 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageLayout/MWMPlacePageLayout.mm +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/MWMPlacePageLayout.mm @@ -61,6 +61,7 @@ map const kMetaInfoCells = { @property(weak, nonatomic) MWMPlacePageTaxiCell * taxiCell; @property(weak, nonatomic) MWMPPViatorCarouselCell * viatorCell; +@property(weak, nonatomic) MWMPPCianCarouselCell * cianCell; @property(nonatomic) BOOL buttonsSectionEnabled; @@ -98,6 +99,7 @@ map const kMetaInfoCells = { [tv registerWithCellClass:[MWMPPHotelDescriptionCell class]]; [tv registerWithCellClass:[MWMPPHotelCarouselCell class]]; [tv registerWithCellClass:[MWMPPViatorCarouselCell class]]; + [tv registerWithCellClass:[MWMPPCianCarouselCell class]]; [tv registerWithCellClass:[MWMPPReviewHeaderCell class]]; [tv registerWithCellClass:[MWMPPReviewCell class]]; [tv registerWithCellClass:[MWMPPFacilityCell class]]; @@ -121,6 +123,7 @@ map const kMetaInfoCells = { dispatch_async(dispatch_get_main_queue(), ^{ [data fillOnlineBookingSections]; [data fillOnlineViatorSection]; + [data fillOnlineCianSection]; }); } @@ -405,12 +408,27 @@ map const kMetaInfoCells = { } case Sections::SpecialProjects: { - Class cls = [MWMPPViatorCarouselCell class]; - auto c = static_cast( - [tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); - [c configWith:data.viatorItems delegate:delegate]; - self.viatorCell = c; - return c; + switch (data.specialProjectRows[indexPath.row]) + { + case SpecialProject::Viator: + { + Class cls = [MWMPPViatorCarouselCell class]; + auto c = static_cast( + [tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); + [c configWith:data.viatorItems delegate:delegate]; + self.viatorCell = c; + return c; + } + case SpecialProject::Cian: + { + Class cls = [MWMPPCianCarouselCell class]; + auto c = static_cast( + [tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); + [c configWithDelegate:delegate]; + self.cianCell = c; + return c; + } + } } case Sections::HotelPhotos: { @@ -659,6 +677,10 @@ map const kMetaInfoCells = { [self.previewLayoutHelper insertRowAtTheEnd]; }; + data.cianIsReadyCallback = ^(NSArray * items) { + self.cianCell.data = items; + }; + [self.actionBar configureWithData:data]; [self.previewLayoutHelper configWithData:data]; auto const & metaInfo = data.metainfoRows;