From 7889a2a982e59d0ed6fdb7988a387201f6ebeac7 Mon Sep 17 00:00:00 2001 From: Zoia Pribytkova Date: Wed, 29 May 2019 16:17:44 +0300 Subject: [PATCH] [iOS][Cross-Traffic] Guides on the discovery screen --- .../Common/Statistics/StatisticsStrings.h | 1 + .../Contents.json | 12 + .../Illustration.pdf | Bin 0 -> 21036 bytes iphone/Maps/Maps.xcodeproj/project.pbxproj | 124 +++-- .../DiscoveryBookingCell.swift | 0 .../DiscoveryBookingCell.xib | 0 .../Collection Cells/DiscoveryGuideCell.swift | 100 ++++ .../Collection Cells/DiscoveryGuideCell.xib | 111 +++++ .../DiscoveryMoreCell.swift | 0 .../DiscoveryMoreCell.xib | 0 .../DiscoverySearchCell.swift | 0 .../DiscoverySearchCell.xib | 0 .../DiscoveryControllerViewModel.hpp | 126 ----- .../Discovery/DiscoveryLocalExpertCell.swift | 74 --- .../UI/Discovery/DiscoveryLocalExpertCell.xib | 131 ----- .../DiscoveryOnlineTemplateCell.swift | 39 -- .../Discovery/DiscoveryOnlineTemplateCell.xib | 93 ---- .../MWMDiscoveryCityGalleryObjects.h | 14 + .../MWMDiscoveryCityGalleryObjects.mm | 35 ++ .../UI/Discovery/MWMDiscoveryController.mm | 322 +++++++----- .../UI/Discovery/MWMDiscoveryMapObjects.h | 23 + .../UI/Discovery/MWMDiscoveryMapObjects.mm | 43 ++ .../UI/Discovery/MWMDiscoveryTableManager.h | 9 +- .../UI/Discovery/MWMDiscoveryTableManager.mm | 466 +++++++----------- .../UI/Discovery/MWMDiscoveryTapDelegate.h | 5 +- .../DiscoveryNoResultsCell.swift | 0 .../DiscoveryNoResultsCell.xib | 0 .../DiscoveryOnlineTemplateCell.swift | 67 +++ .../DiscoveryOnlineTemplateCell.xib | 122 +++++ .../DiscoverySpinnerCell.swift | 0 .../DiscoverySpinnerCell.xib | 0 .../DiscoveryBookingCollectionHolderCell.xib | 0 .../DiscoveryCollectionHolderCell.swift | 18 +- .../DiscoveryGuideCollectionHolderCell.xib} | 59 +-- .../DiscoverySearchCollectionHolderCell.xib | 0 .../MWMDiscoveryControllerViewModel.h | 30 ++ .../MWMDiscoveryControllerViewModel.mm | 184 +++++++ .../View Models/MWMDiscoveryGuideViewModel.h | 17 + .../View Models/MWMDiscoveryGuideViewModel.m | 28 ++ .../View Models/MWMDiscoveryHotelViewModel.h | 25 + .../View Models/MWMDiscoveryHotelViewModel.m | 37 ++ .../View Models/MWMDiscoverySearchViewModel.h | 22 + .../View Models/MWMDiscoverySearchViewModel.m | 34 ++ 43 files changed, 1419 insertions(+), 952 deletions(-) create mode 100644 iphone/Maps/Images.xcassets/Discovery/img_guide_placeholder.imageset/Contents.json create mode 100644 iphone/Maps/Images.xcassets/Discovery/img_guide_placeholder.imageset/Illustration.pdf rename iphone/Maps/UI/Discovery/{ => Collection Cells}/DiscoveryBookingCell.swift (100%) rename iphone/Maps/UI/Discovery/{ => Collection Cells}/DiscoveryBookingCell.xib (100%) create mode 100644 iphone/Maps/UI/Discovery/Collection Cells/DiscoveryGuideCell.swift create mode 100644 iphone/Maps/UI/Discovery/Collection Cells/DiscoveryGuideCell.xib rename iphone/Maps/UI/Discovery/{ => Collection Cells}/DiscoveryMoreCell.swift (100%) rename iphone/Maps/UI/Discovery/{ => Collection Cells}/DiscoveryMoreCell.xib (100%) rename iphone/Maps/UI/Discovery/{ => Collection Cells}/DiscoverySearchCell.swift (100%) rename iphone/Maps/UI/Discovery/{ => Collection Cells}/DiscoverySearchCell.xib (100%) delete mode 100644 iphone/Maps/UI/Discovery/DiscoveryControllerViewModel.hpp delete mode 100644 iphone/Maps/UI/Discovery/DiscoveryLocalExpertCell.swift delete mode 100644 iphone/Maps/UI/Discovery/DiscoveryLocalExpertCell.xib delete mode 100644 iphone/Maps/UI/Discovery/DiscoveryOnlineTemplateCell.swift delete mode 100644 iphone/Maps/UI/Discovery/DiscoveryOnlineTemplateCell.xib create mode 100644 iphone/Maps/UI/Discovery/MWMDiscoveryCityGalleryObjects.h create mode 100644 iphone/Maps/UI/Discovery/MWMDiscoveryCityGalleryObjects.mm create mode 100644 iphone/Maps/UI/Discovery/MWMDiscoveryMapObjects.h create mode 100644 iphone/Maps/UI/Discovery/MWMDiscoveryMapObjects.mm rename iphone/Maps/UI/Discovery/{ => Table Cells}/DiscoveryNoResultsCell.swift (100%) rename iphone/Maps/UI/Discovery/{ => Table Cells}/DiscoveryNoResultsCell.xib (100%) create mode 100644 iphone/Maps/UI/Discovery/Table Cells/DiscoveryOnlineTemplateCell.swift create mode 100644 iphone/Maps/UI/Discovery/Table Cells/DiscoveryOnlineTemplateCell.xib rename iphone/Maps/UI/Discovery/{ => Table Cells}/DiscoverySpinnerCell.swift (100%) rename iphone/Maps/UI/Discovery/{ => Table Cells}/DiscoverySpinnerCell.xib (100%) rename iphone/Maps/UI/Discovery/{ => Table Cells/Holders}/DiscoveryBookingCollectionHolderCell.xib (100%) rename iphone/Maps/UI/Discovery/{ => Table Cells/Holders}/DiscoveryCollectionHolderCell.swift (80%) rename iphone/Maps/UI/Discovery/{DiscoveryLocalExpertCollectionHolderCell.xib => Table Cells/Holders/DiscoveryGuideCollectionHolderCell.xib} (55%) rename iphone/Maps/UI/Discovery/{ => Table Cells/Holders}/DiscoverySearchCollectionHolderCell.xib (100%) create mode 100644 iphone/Maps/UI/Discovery/View Models/MWMDiscoveryControllerViewModel.h create mode 100644 iphone/Maps/UI/Discovery/View Models/MWMDiscoveryControllerViewModel.mm create mode 100644 iphone/Maps/UI/Discovery/View Models/MWMDiscoveryGuideViewModel.h create mode 100644 iphone/Maps/UI/Discovery/View Models/MWMDiscoveryGuideViewModel.m create mode 100644 iphone/Maps/UI/Discovery/View Models/MWMDiscoveryHotelViewModel.h create mode 100644 iphone/Maps/UI/Discovery/View Models/MWMDiscoveryHotelViewModel.m create mode 100644 iphone/Maps/UI/Discovery/View Models/MWMDiscoverySearchViewModel.h create mode 100644 iphone/Maps/UI/Discovery/View Models/MWMDiscoverySearchViewModel.m diff --git a/iphone/Maps/Common/Statistics/StatisticsStrings.h b/iphone/Maps/Common/Statistics/StatisticsStrings.h index 04205a1591..7b21e92acd 100644 --- a/iphone/Maps/Common/Statistics/StatisticsStrings.h +++ b/iphone/Maps/Common/Statistics/StatisticsStrings.h @@ -213,6 +213,7 @@ static NSString * const kStatMapSearch = @"Map search"; static NSString * const kStatMapViewStyle = @"Map view style"; static NSString * const kStatMapViewStyleSettings = @"Map view style settings"; static NSString * const kStatMapsme = @"maps.me"; +static NSString * const kStatMapsmeGuides = @"MapsMeGuides"; static NSString * const kStatMaxim = @"Maxim"; static NSString * const kStatMenu = @"menu"; static NSString * const kStatMigrationBig2SmallMWM = @"Big mwms to small mwms migration counter"; diff --git a/iphone/Maps/Images.xcassets/Discovery/img_guide_placeholder.imageset/Contents.json b/iphone/Maps/Images.xcassets/Discovery/img_guide_placeholder.imageset/Contents.json new file mode 100644 index 0000000000..04b61f1afd --- /dev/null +++ b/iphone/Maps/Images.xcassets/Discovery/img_guide_placeholder.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Illustration.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Discovery/img_guide_placeholder.imageset/Illustration.pdf b/iphone/Maps/Images.xcassets/Discovery/img_guide_placeholder.imageset/Illustration.pdf new file mode 100644 index 0000000000000000000000000000000000000000..407a5e0b96dfa7f4ba3ca2f74301b27b4c9c79f4 GIT binary patch literal 21036 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 F+W!pgk=XzM literal 0 HcmV?d00001 diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index a480b8c827..8d1328a0a4 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -568,10 +568,18 @@ BB8123CF212C264700ADE512 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB8123CD212C264700ADE512 /* Metal.framework */; }; BB8123D0212C264700ADE512 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB8123CE212C264700ADE512 /* MetalKit.framework */; }; BB8123D62130427E00ADE512 /* MetalContextFactory.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB8123D52130427E00ADE512 /* MetalContextFactory.mm */; }; + CD96C70C22A681C400DB7CFE /* DiscoveryGuideCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD96C70A22A681C400DB7CFE /* DiscoveryGuideCell.swift */; }; + CD96C70D22A681C400DB7CFE /* DiscoveryGuideCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD96C70B22A681C400DB7CFE /* DiscoveryGuideCell.xib */; }; + CD96C71122A6820800DB7CFE /* DiscoveryGuideCollectionHolderCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD96C70F22A6820800DB7CFE /* DiscoveryGuideCollectionHolderCell.xib */; }; + CD96C71422A6CBFD00DB7CFE /* MWMDiscoveryGuideViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = CD96C71322A6CBFD00DB7CFE /* MWMDiscoveryGuideViewModel.m */; }; + CD96C71722A7B5DE00DB7CFE /* MWMDiscoveryCityGalleryObjects.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD96C71622A7B5DE00DB7CFE /* MWMDiscoveryCityGalleryObjects.mm */; }; + CD96C71C22A8113100DB7CFE /* MWMDiscoveryControllerViewModel.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDB92CEA229E9ADF00EC757C /* MWMDiscoveryControllerViewModel.mm */; }; + CDB92CEE229E9CF900EC757C /* MWMDiscoveryMapObjects.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDB92CED229E9CF900EC757C /* MWMDiscoveryMapObjects.mm */; }; + CDB92CF1229EB8A800EC757C /* MWMDiscoverySearchViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = CDB92CF0229EB8A800EC757C /* MWMDiscoverySearchViewModel.m */; }; + CDB92CF822A5350500EC757C /* MWMDiscoveryHotelViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = CDB92CF722A5350500EC757C /* MWMDiscoveryHotelViewModel.m */; }; F5BD255A0838E70EC012748E /* DiscoverySearchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5BD2ED6E94925472A9901B4 /* DiscoverySearchCell.swift */; }; F5BD29FF26AD58255766C51A /* DiscoverySpinnerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5BD246A7E6BE8CD8600EDD9 /* DiscoverySpinnerCell.swift */; }; F5BD2CA4DBEFACBC48195F39 /* DiscoveryCollectionHolderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5BD2A86D9DA2F9769D30B54 /* DiscoveryCollectionHolderCell.swift */; }; - F603E05A1FDE9410006B84D6 /* DiscoveryLocalExpertCollectionHolderCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F603E0581FDE9410006B84D6 /* DiscoveryLocalExpertCollectionHolderCell.xib */; }; F603E05E1FDE9703006B84D6 /* DiscoverySearchCollectionHolderCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F603E05C1FDE9703006B84D6 /* DiscoverySearchCollectionHolderCell.xib */; }; F607C1881C032A8800B53A87 /* resources-hdpi_clear in Resources */ = {isa = PBXBuildFile; fileRef = F607C1831C032A8800B53A87 /* resources-hdpi_clear */; }; F607C18A1C032A8800B53A87 /* resources-hdpi_dark in Resources */ = {isa = PBXBuildFile; fileRef = F607C1841C032A8800B53A87 /* resources-hdpi_dark */; }; @@ -614,8 +622,6 @@ F69018BD1E9F7CB600B3C10B /* MWMAutoupdateController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F69018BB1E9F7CB600B3C10B /* MWMAutoupdateController.xib */; }; F692F3831EA0FAF5001E82EB /* MWMAutoupdateController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F69018B71E9E601400B3C10B /* MWMAutoupdateController.mm */; }; F69739B21FD197DB00FDA07D /* MWMDiscoveryTableManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = F69739B01FD197DB00FDA07D /* MWMDiscoveryTableManager.mm */; }; - F69739DC1FD6ECCE00FDA07D /* DiscoveryLocalExpertCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F69739DA1FD6ECCE00FDA07D /* DiscoveryLocalExpertCell.swift */; }; - F69739E01FD6EE1D00FDA07D /* DiscoveryLocalExpertCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F69739DE1FD6EE1D00FDA07D /* DiscoveryLocalExpertCell.xib */; }; F69CE8D61E5C49B4002B5881 /* PPHotelCarouselCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F69CE8D41E5C49B4002B5881 /* PPHotelCarouselCell.swift */; }; F69CE8DA1E5C5088002B5881 /* PPHotelCarouselCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F69CE8D81E5C5088002B5881 /* PPHotelCarouselCell.xib */; }; F69CE8DE1E5C51AB002B5881 /* CarouselElement.xib in Resources */ = {isa = PBXBuildFile; fileRef = F69CE8DC1E5C51AB002B5881 /* CarouselElement.xib */; }; @@ -1598,6 +1604,21 @@ BB8123CE212C264700ADE512 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; }; BB8123D42130427E00ADE512 /* MetalContextFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalContextFactory.h; sourceTree = ""; }; BB8123D52130427E00ADE512 /* MetalContextFactory.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MetalContextFactory.mm; sourceTree = ""; }; + CD96C70A22A681C400DB7CFE /* DiscoveryGuideCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryGuideCell.swift; sourceTree = ""; }; + CD96C70B22A681C400DB7CFE /* DiscoveryGuideCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryGuideCell.xib; sourceTree = ""; }; + CD96C70F22A6820800DB7CFE /* DiscoveryGuideCollectionHolderCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryGuideCollectionHolderCell.xib; sourceTree = ""; }; + CD96C71222A6CBFD00DB7CFE /* MWMDiscoveryGuideViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryGuideViewModel.h; sourceTree = ""; }; + CD96C71322A6CBFD00DB7CFE /* MWMDiscoveryGuideViewModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMDiscoveryGuideViewModel.m; sourceTree = ""; }; + CD96C71522A7B5DE00DB7CFE /* MWMDiscoveryCityGalleryObjects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryCityGalleryObjects.h; sourceTree = ""; }; + CD96C71622A7B5DE00DB7CFE /* MWMDiscoveryCityGalleryObjects.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMDiscoveryCityGalleryObjects.mm; sourceTree = ""; }; + CDB92CE9229E9ADF00EC757C /* MWMDiscoveryControllerViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryControllerViewModel.h; sourceTree = ""; }; + CDB92CEA229E9ADF00EC757C /* MWMDiscoveryControllerViewModel.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMDiscoveryControllerViewModel.mm; sourceTree = ""; }; + CDB92CEC229E9CF900EC757C /* MWMDiscoveryMapObjects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryMapObjects.h; sourceTree = ""; }; + CDB92CED229E9CF900EC757C /* MWMDiscoveryMapObjects.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMDiscoveryMapObjects.mm; sourceTree = ""; }; + CDB92CEF229EB8A700EC757C /* MWMDiscoverySearchViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoverySearchViewModel.h; sourceTree = ""; }; + CDB92CF0229EB8A800EC757C /* MWMDiscoverySearchViewModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMDiscoverySearchViewModel.m; sourceTree = ""; }; + CDB92CF622A5350500EC757C /* MWMDiscoveryHotelViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryHotelViewModel.h; sourceTree = ""; }; + CDB92CF722A5350500EC757C /* MWMDiscoveryHotelViewModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMDiscoveryHotelViewModel.m; sourceTree = ""; }; ED48BBB317C267F5003E7E92 /* ColorPickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorPickerView.h; sourceTree = ""; }; ED48BBB417C267F5003E7E92 /* ColorPickerView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ColorPickerView.mm; sourceTree = ""; }; ED48BBB817C2B1E2003E7E92 /* CircleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircleView.h; sourceTree = ""; }; @@ -1615,7 +1636,6 @@ F5BD246A7E6BE8CD8600EDD9 /* DiscoverySpinnerCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscoverySpinnerCell.swift; sourceTree = ""; }; F5BD2A86D9DA2F9769D30B54 /* DiscoveryCollectionHolderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscoveryCollectionHolderCell.swift; sourceTree = ""; }; F5BD2ED6E94925472A9901B4 /* DiscoverySearchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscoverySearchCell.swift; sourceTree = ""; }; - F603E0581FDE9410006B84D6 /* DiscoveryLocalExpertCollectionHolderCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryLocalExpertCollectionHolderCell.xib; sourceTree = ""; }; F603E05C1FDE9703006B84D6 /* DiscoverySearchCollectionHolderCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoverySearchCollectionHolderCell.xib; sourceTree = ""; }; F607C1831C032A8800B53A87 /* resources-hdpi_clear */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-hdpi_clear"; path = "../../data/resources-hdpi_clear"; sourceTree = ""; }; F607C1841C032A8800B53A87 /* resources-hdpi_dark */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-hdpi_dark"; path = "../../data/resources-hdpi_dark"; sourceTree = ""; }; @@ -1695,10 +1715,7 @@ F69018BB1E9F7CB600B3C10B /* MWMAutoupdateController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMAutoupdateController.xib; sourceTree = ""; }; F69739AF1FD197DB00FDA07D /* MWMDiscoveryTableManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryTableManager.h; sourceTree = ""; }; F69739B01FD197DB00FDA07D /* MWMDiscoveryTableManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMDiscoveryTableManager.mm; sourceTree = ""; }; - F69739B41FD198E300FDA07D /* DiscoveryControllerViewModel.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiscoveryControllerViewModel.hpp; sourceTree = ""; }; F69739B51FD19D9900FDA07D /* MWMDiscoveryTapDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryTapDelegate.h; sourceTree = ""; }; - F69739DA1FD6ECCE00FDA07D /* DiscoveryLocalExpertCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryLocalExpertCell.swift; sourceTree = ""; }; - F69739DE1FD6EE1D00FDA07D /* DiscoveryLocalExpertCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryLocalExpertCell.xib; sourceTree = ""; }; F69CE8D41E5C49B4002B5881 /* PPHotelCarouselCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PPHotelCarouselCell.swift; sourceTree = ""; }; F69CE8D81E5C5088002B5881 /* PPHotelCarouselCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PPHotelCarouselCell.xib; sourceTree = ""; }; F69CE8DC1E5C51AB002B5881 /* CarouselElement.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CarouselElement.xib; sourceTree = ""; }; @@ -3512,6 +3529,61 @@ path = Catalog; sourceTree = ""; }; + CD96C71822A80C8400DB7CFE /* Collection Cells */ = { + isa = PBXGroup; + children = ( + CD96C70A22A681C400DB7CFE /* DiscoveryGuideCell.swift */, + CD96C70B22A681C400DB7CFE /* DiscoveryGuideCell.xib */, + 34B6FD5D2015E6BE00C18E97 /* DiscoveryBookingCell.swift */, + 34B6FD5E2015E6BF00C18E97 /* DiscoveryBookingCell.xib */, + F5BD2ED6E94925472A9901B4 /* DiscoverySearchCell.swift */, + F60C8BED1FCED15900DCF5FB /* DiscoverySearchCell.xib */, + F655C026207278300048A241 /* DiscoveryMoreCell.swift */, + F655C02820727A630048A241 /* DiscoveryMoreCell.xib */, + ); + path = "Collection Cells"; + sourceTree = ""; + }; + CD96C71922A80CCF00DB7CFE /* Table Cells */ = { + isa = PBXGroup; + children = ( + CD96C71A22A80D1600DB7CFE /* Holders */, + F6EBB26D1FD7E33300B69B6A /* DiscoveryNoResultsCell.swift */, + F6EBB2711FD7E4FD00B69B6A /* DiscoveryNoResultsCell.xib */, + F61757EB1FC73027000AD0D0 /* DiscoveryOnlineTemplateCell.swift */, + F61757EF1FC731F5000AD0D0 /* DiscoveryOnlineTemplateCell.xib */, + F5BD246A7E6BE8CD8600EDD9 /* DiscoverySpinnerCell.swift */, + F61757E71FC72CDE000AD0D0 /* DiscoverySpinnerCell.xib */, + ); + path = "Table Cells"; + sourceTree = ""; + }; + CD96C71A22A80D1600DB7CFE /* Holders */ = { + isa = PBXGroup; + children = ( + CD96C70F22A6820800DB7CFE /* DiscoveryGuideCollectionHolderCell.xib */, + 34B6FD612015F71900C18E97 /* DiscoveryBookingCollectionHolderCell.xib */, + F603E05C1FDE9703006B84D6 /* DiscoverySearchCollectionHolderCell.xib */, + F5BD2A86D9DA2F9769D30B54 /* DiscoveryCollectionHolderCell.swift */, + ); + path = Holders; + sourceTree = ""; + }; + CD96C71B22A80D3E00DB7CFE /* View Models */ = { + isa = PBXGroup; + children = ( + CDB92CE9229E9ADF00EC757C /* MWMDiscoveryControllerViewModel.h */, + CDB92CEA229E9ADF00EC757C /* MWMDiscoveryControllerViewModel.mm */, + CDB92CEF229EB8A700EC757C /* MWMDiscoverySearchViewModel.h */, + CDB92CF0229EB8A800EC757C /* MWMDiscoverySearchViewModel.m */, + CDB92CF622A5350500EC757C /* MWMDiscoveryHotelViewModel.h */, + CDB92CF722A5350500EC757C /* MWMDiscoveryHotelViewModel.m */, + CD96C71222A6CBFD00DB7CFE /* MWMDiscoveryGuideViewModel.h */, + CD96C71322A6CBFD00DB7CFE /* MWMDiscoveryGuideViewModel.m */, + ); + path = "View Models"; + sourceTree = ""; + }; F607C18B1C047FCA00B53A87 /* Segue */ = { isa = PBXGroup; children = ( @@ -4310,31 +4382,19 @@ F6E407CC1FC45ED4001F7821 /* Discovery */ = { isa = PBXGroup; children = ( - 34B6FD5D2015E6BE00C18E97 /* DiscoveryBookingCell.swift */, - 34B6FD5E2015E6BF00C18E97 /* DiscoveryBookingCell.xib */, - 34B6FD612015F71900C18E97 /* DiscoveryBookingCollectionHolderCell.xib */, - F5BD2A86D9DA2F9769D30B54 /* DiscoveryCollectionHolderCell.swift */, - F69739B41FD198E300FDA07D /* DiscoveryControllerViewModel.hpp */, - F69739DA1FD6ECCE00FDA07D /* DiscoveryLocalExpertCell.swift */, - F69739DE1FD6EE1D00FDA07D /* DiscoveryLocalExpertCell.xib */, - F603E0581FDE9410006B84D6 /* DiscoveryLocalExpertCollectionHolderCell.xib */, - F6EBB26D1FD7E33300B69B6A /* DiscoveryNoResultsCell.swift */, - F6EBB2711FD7E4FD00B69B6A /* DiscoveryNoResultsCell.xib */, - F61757EB1FC73027000AD0D0 /* DiscoveryOnlineTemplateCell.swift */, - F61757EF1FC731F5000AD0D0 /* DiscoveryOnlineTemplateCell.xib */, - F5BD2ED6E94925472A9901B4 /* DiscoverySearchCell.swift */, - F60C8BED1FCED15900DCF5FB /* DiscoverySearchCell.xib */, - F603E05C1FDE9703006B84D6 /* DiscoverySearchCollectionHolderCell.xib */, - F5BD246A7E6BE8CD8600EDD9 /* DiscoverySpinnerCell.swift */, - F61757E71FC72CDE000AD0D0 /* DiscoverySpinnerCell.xib */, + CD96C71B22A80D3E00DB7CFE /* View Models */, + CD96C71922A80CCF00DB7CFE /* Table Cells */, + CD96C71822A80C8400DB7CFE /* Collection Cells */, F6E407CD1FC45EF5001F7821 /* MWMDiscoveryController.h */, F6E407CE1FC45EF5001F7821 /* MWMDiscoveryController.mm */, F6E407D21FC4722F001F7821 /* MWMDiscoveryController.xib */, F69739AF1FD197DB00FDA07D /* MWMDiscoveryTableManager.h */, F69739B01FD197DB00FDA07D /* MWMDiscoveryTableManager.mm */, F69739B51FD19D9900FDA07D /* MWMDiscoveryTapDelegate.h */, - F655C026207278300048A241 /* DiscoveryMoreCell.swift */, - F655C02820727A630048A241 /* DiscoveryMoreCell.xib */, + CDB92CEC229E9CF900EC757C /* MWMDiscoveryMapObjects.h */, + CDB92CED229E9CF900EC757C /* MWMDiscoveryMapObjects.mm */, + CD96C71522A7B5DE00DB7CFE /* MWMDiscoveryCityGalleryObjects.h */, + CD96C71622A7B5DE00DB7CFE /* MWMDiscoveryCityGalleryObjects.mm */, ); path = Discovery; sourceTree = ""; @@ -4623,6 +4683,7 @@ 349D1AE11E2E325C004A2006 /* MWMBottomMenuViewController.xib in Resources */, 34D3B01E1E389D05004100F9 /* MWMButtonCell.xib in Resources */, 6741A98B1BF340DE002C974C /* MWMCircularProgress.xib in Resources */, + CD96C71122A6820800DB7CFE /* DiscoveryGuideCollectionHolderCell.xib in Resources */, 6741A94F1BF340DE002C974C /* MWMDefaultAlert.xib in Resources */, F6E2FE461E097BA00083EBEC /* MWMDirectionView.xib in Resources */, 6741A9951BF340DE002C974C /* MWMDownloaderDialogCell.xib in Resources */, @@ -4669,7 +4730,7 @@ 34B6FD602015E6BF00C18E97 /* DiscoveryBookingCell.xib in Resources */, 4788739020EE30B300F6826B /* LayersViewController.xib in Resources */, F6E2FDF51E097BA00083EBEC /* MWMOpeningHoursAddScheduleTableViewCell.xib in Resources */, - F603E05A1FDE9410006B84D6 /* DiscoveryLocalExpertCollectionHolderCell.xib in Resources */, + CD96C70D22A681C400DB7CFE /* DiscoveryGuideCell.xib in Resources */, F6E2FDFB1E097BA00083EBEC /* MWMOpeningHoursAllDayTableViewCell.xib in Resources */, 342639361EA0E60A0025EB89 /* local_ads_symbols.txt in Resources */, 4554B6EC1E55F0EF0084017F /* drules_proto_vehicle_clear.bin in Resources */, @@ -4748,7 +4809,6 @@ F69CE8DA1E5C5088002B5881 /* PPHotelCarouselCell.xib in Resources */, F69CE8DE1E5C51AB002B5881 /* CarouselElement.xib in Resources */, 6741A97F1BF340DE002C974C /* resources-mdpi_clear in Resources */, - F69739E01FD6EE1D00FDA07D /* DiscoveryLocalExpertCell.xib in Resources */, 6741A9901BF340DE002C974C /* resources-mdpi_dark in Resources */, 6741A9981BF340DE002C974C /* resources-xhdpi_clear in Resources */, 6741A9611BF340DE002C974C /* resources-xhdpi_dark in Resources */, @@ -4894,6 +4954,7 @@ 34AB665F1FC5AA330078E451 /* TransportTransitIntermediatePoint.swift in Sources */, 34B846A82029E8110081ECCD /* BMCDefaultViewModel.swift in Sources */, 470F5A7F2189C30800754295 /* InAppPurchase.swift in Sources */, + CD96C71422A6CBFD00DB7CFE /* MWMDiscoveryGuideViewModel.m in Sources */, 348A8DF51F66775A00D83026 /* RatingView.swift in Sources */, F63AF50F1EA6215100A1DB98 /* FilterPriceCategoryCell.swift in Sources */, 34D3AFF61E37A36A004100F9 /* UICollectionView+Cells.swift in Sources */, @@ -4930,6 +4991,7 @@ 34F4073E1E9E1AFF00E57AC0 /* MPNativeAd+MWM.mm in Sources */, F6E2FED01E097BA00083EBEC /* MWMSearchFilterViewController.mm in Sources */, 34D4FA671E265749003F53EF /* WhatsNewController.swift in Sources */, + CDB92CEE229E9CF900EC757C /* MWMDiscoveryMapObjects.mm in Sources */, 34B6FD5F2015E6BF00C18E97 /* DiscoveryBookingCell.swift in Sources */, 34D3B01B1E389D05004100F9 /* MWMButtonCell.mm in Sources */, 3486B5161E27AD3B0069C126 /* Framework.cpp in Sources */, @@ -4960,7 +5022,6 @@ 34E50DF81F6FCC96008EED49 /* UGCReviewCell.swift in Sources */, F6E2FF3F1E097BA00083EBEC /* MWMSearchTableViewController.mm in Sources */, F6E2FDE01E097BA00083EBEC /* MWMEditorViewController.mm in Sources */, - F69739DC1FD6ECCE00FDA07D /* DiscoveryLocalExpertCell.swift in Sources */, 6741A9C01BF340DE002C974C /* MWMTextView.m in Sources */, B32FE74320D2B09600EF7446 /* CatalogWebViewController.swift in Sources */, F6E2FDB61E097BA00083EBEC /* MWMEditorAdditionalNamesHeader.mm in Sources */, @@ -5031,6 +5092,7 @@ 349D1ACF1E2E325B004A2006 /* MWMBottomMenuCollectionViewCell.mm in Sources */, F6E2FF451E097BA00083EBEC /* SettingsTableViewLinkCell.swift in Sources */, 34C9BD0A1C6DBCDA000DC38D /* MWMNavigationController.m in Sources */, + CDB92CF822A5350500EC757C /* MWMDiscoveryHotelViewModel.m in Sources */, F6550C1F1FD81B3800352D88 /* RatingSummaryView+DefaultConfig.swift in Sources */, F6E2FE311E097BA00083EBEC /* MWMStreetEditorViewController.mm in Sources */, F6E2FE281E097BA00083EBEC /* MWMOpeningHoursSection.mm in Sources */, @@ -5115,6 +5177,7 @@ B33D21B820E130D000BAD749 /* BookmarksTabViewController.swift in Sources */, 3404754A1E081A4600C92850 /* AppInfo.mm in Sources */, 3358607E217632A2006D11F2 /* BookmarksSharingViewController.swift in Sources */, + CDB92CF1229EB8A800EC757C /* MWMDiscoverySearchViewModel.m in Sources */, 34AB662F1FC5AA330078E451 /* RouteManageriPhonePresentationController.swift in Sources */, 4797A4DC226F4B2900D3A984 /* DeepLinkHandler.swift in Sources */, 34AB66201FC5AA330078E451 /* RouteStartButton.swift in Sources */, @@ -5146,6 +5209,7 @@ 34E7761F1F14DB48003040B3 /* PlacePageArea.swift in Sources */, 346DB82E1E5C4F6700E3123E /* GalleryItemViewController.swift in Sources */, 340475561E081A4600C92850 /* Statistics.mm in Sources */, + CD96C71722A7B5DE00DB7CFE /* MWMDiscoveryCityGalleryObjects.mm in Sources */, F6381BF61CD12045004CA943 /* LocaleTranslator.mm in Sources */, 3444DFCD1F1760B900E73099 /* WidgetsArea.swift in Sources */, F6E2FE9A1E097BA00083EBEC /* MWMiPadPlacePageLayoutImpl.mm in Sources */, @@ -5237,6 +5301,7 @@ 349D1ADB1E2E325C004A2006 /* MWMBottomMenuView.mm in Sources */, 344BEAF61F66BDC30045DC45 /* RatingSummaryViewSettings.swift in Sources */, F6E2FD921E097BA00083EBEC /* MWMBookmarkColorViewController.mm in Sources */, + CD96C71C22A8113100DB7CFE /* MWMDiscoveryControllerViewModel.mm in Sources */, F63AF5061EA6162400A1DB98 /* FilterTypeCell.swift in Sources */, 347752881F725002000D46A3 /* UGCAddReviewRatingCell.swift in Sources */, 47E3C7332111F4D8008B3B27 /* CoverVerticalDismissalAnimator.swift in Sources */, @@ -5287,6 +5352,7 @@ 34EF94291C05A6F30050B714 /* MWMSegue.m in Sources */, 3430291D1F87BF4400D0A07C /* ReviewsViewController.swift in Sources */, F6E2FE731E097BA00083EBEC /* MWMOpeningHours.mm in Sources */, + CD96C70C22A681C400DB7CFE /* DiscoveryGuideCell.swift in Sources */, 47E3C7312111F4C2008B3B27 /* CoverVerticalPresentationAnimator.swift in Sources */, F6D67CDC2062B9C00032FD38 /* BCCreateCategoryAlert.swift in Sources */, F6E2FF601E097BA00083EBEC /* MWMSettingsViewController.mm in Sources */, diff --git a/iphone/Maps/UI/Discovery/DiscoveryBookingCell.swift b/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryBookingCell.swift similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoveryBookingCell.swift rename to iphone/Maps/UI/Discovery/Collection Cells/DiscoveryBookingCell.swift diff --git a/iphone/Maps/UI/Discovery/DiscoveryBookingCell.xib b/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryBookingCell.xib similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoveryBookingCell.xib rename to iphone/Maps/UI/Discovery/Collection Cells/DiscoveryBookingCell.xib diff --git a/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryGuideCell.swift b/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryGuideCell.swift new file mode 100644 index 0000000000..2f0170b7b6 --- /dev/null +++ b/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryGuideCell.swift @@ -0,0 +1,100 @@ +@objc(MWMDiscoveryGuideCell) +final class DiscoveryGuideCell: UICollectionViewCell { + @IBOutlet var avatar: UIImageView! + @IBOutlet var titleLabel: UILabel! { + didSet { + titleLabel.font = UIFont.medium14() + titleLabel.textColor = UIColor.blackPrimaryText() + titleLabel.numberOfLines = 2 + } + } + + @IBOutlet var subtitleLabel: UILabel! { + didSet { + subtitleLabel.font = UIFont.regular12() + subtitleLabel.textColor = UIColor.blackSecondaryText() + subtitleLabel.numberOfLines = 1 + } + } + + @IBOutlet var proLabel: UILabel! { + didSet { + proLabel.font = UIFont.bold12() + proLabel.textColor = UIColor.white() + proLabel.backgroundColor = .clear + proLabel.text = ""; + } + } + + @IBOutlet var proContainer: UIView! { + didSet { + proLabel.backgroundColor = UIColor.ratingRed() + } + } + + @IBOutlet var detailsButton: UIButton! { + didSet { + detailsButton.setTitleColor(UIColor.linkBlue(), for: .normal) + detailsButton.setTitle(L("details"), for: .normal) + } + } + + typealias OnDetails = () -> Void + private var onDetails: OnDetails? + + override var isHighlighted: Bool { + didSet { + UIView.animate(withDuration: kDefaultAnimationDuration, + delay: 0, + options: [.allowUserInteraction, .beginFromCurrentState], + animations: { self.alpha = self.isHighlighted ? 0.3 : 1 }, + completion: nil) + } + } + + override func awakeFromNib() { + super.awakeFromNib() + layer.borderColor = UIColor.blackDividers().cgColor + } + + override func prepareForReuse() { + super.prepareForReuse() + avatar.image = UIImage(named: "img_guide_placeholder") + titleLabel.text = "" + subtitleLabel.text = "" + proLabel.text = "" + proContainer.isHidden = true + onDetails = nil + } + + private func setAvatar(_ avatarURL: String?) { + guard let avatarURL = avatarURL else { return } + if !avatarURL.isEmpty, let url = URL(string: avatarURL) { + avatar.image = UIImage(named: "img_guide_placeholder") + avatar.wi_setImage(with: url, transitionDuration: kDefaultAnimationDuration) + } else { + avatar.image = UIImage(named: "img_guide_placeholder") + } + } + + @objc func config(avatarURL: String?, + title: String, + subtitle: String, + label: String?, + onDetails: @escaping OnDetails) { + setAvatar(avatarURL) + titleLabel.text = title + subtitleLabel.text = subtitle + self.onDetails = onDetails + guard let label = label, !label.isEmpty else { + proContainer.isHidden = true + return + } + proLabel.text = label + proContainer.isHidden = false + } + + @IBAction private func detailsAction() { + onDetails?() + } +} diff --git a/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryGuideCell.xib b/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryGuideCell.xib new file mode 100644 index 0000000000..f2d7fa3aff --- /dev/null +++ b/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryGuideCell.xib @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/UI/Discovery/DiscoveryMoreCell.swift b/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryMoreCell.swift similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoveryMoreCell.swift rename to iphone/Maps/UI/Discovery/Collection Cells/DiscoveryMoreCell.swift diff --git a/iphone/Maps/UI/Discovery/DiscoveryMoreCell.xib b/iphone/Maps/UI/Discovery/Collection Cells/DiscoveryMoreCell.xib similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoveryMoreCell.xib rename to iphone/Maps/UI/Discovery/Collection Cells/DiscoveryMoreCell.xib diff --git a/iphone/Maps/UI/Discovery/DiscoverySearchCell.swift b/iphone/Maps/UI/Discovery/Collection Cells/DiscoverySearchCell.swift similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoverySearchCell.swift rename to iphone/Maps/UI/Discovery/Collection Cells/DiscoverySearchCell.swift diff --git a/iphone/Maps/UI/Discovery/DiscoverySearchCell.xib b/iphone/Maps/UI/Discovery/Collection Cells/DiscoverySearchCell.xib similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoverySearchCell.xib rename to iphone/Maps/UI/Discovery/Collection Cells/DiscoverySearchCell.xib diff --git a/iphone/Maps/UI/Discovery/DiscoveryControllerViewModel.hpp b/iphone/Maps/UI/Discovery/DiscoveryControllerViewModel.hpp deleted file mode 100644 index fe83be1e16..0000000000 --- a/iphone/Maps/UI/Discovery/DiscoveryControllerViewModel.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once - -#include "map/discovery/discovery_client_params.hpp" -#include "map/search_product_info.hpp" - -#include "partners_api/locals_api.hpp" - -#include "search/result.hpp" - -#include "geometry/point2d.hpp" - -#include "base/assert.hpp" - -#include - -namespace discovery -{ -class DiscoveryControllerViewModel -{ -public: - void SetSearchResults(search::Results const & res, - std::vector const & productInfo, - m2::PointD const & viewportCenter, ItemType const type) - { - switch (type) - { - case ItemType::Attractions: - m_attractions.SetResults(res, productInfo, viewportCenter); - break; - case ItemType::Cafes: - m_cafes.SetResults(res, productInfo, viewportCenter); - break; - case ItemType::Hotels: - m_hotels.SetResults(res, productInfo, viewportCenter); - break; - default: break; - } - } - - void SetExperts(std::vector const & experts) { m_experts = experts; } - - size_t GetItemsCount(ItemType const type) const - { - switch (type) - { - case ItemType::Attractions: return m_attractions.m_results.GetCount(); - case ItemType::Cafes: return m_cafes.m_results.GetCount(); - case ItemType::Hotels: return m_hotels.m_results.GetCount(); - case ItemType::LocalExperts: return m_experts.size(); - // TODO: Add correct value here. - case ItemType::Promo: return 0; - } - } - - search::Result const & GetAttractionAt(size_t const index) const - { - return m_attractions.GetSearchResultAt(index); - } - - m2::PointD const & GetAttractionReferencePoint() const { return m_attractions.m_viewportCenter; } - - search::ProductInfo const & GetAttractionProductInfoAt(size_t const index) const - { - return m_attractions.GetProductInfoAt(index); - } - - search::Result const & GetCafeAt(size_t const index) const - { - return m_cafes.GetSearchResultAt(index); - } - - m2::PointD const & GetCafeReferencePoint() const { return m_cafes.m_viewportCenter; } - - search::ProductInfo const & GetCafeProductInfoAt(size_t const index) const - { - return m_cafes.GetProductInfoAt(index); - } - - locals::LocalExpert const & GetExpertAt(size_t const index) const - { - CHECK_LESS(index, m_experts.size(), ("Incorrect experts index:", index)); - return m_experts[index]; - } - - search::Result const & GetHotelAt(size_t const index) const - { - return m_hotels.GetSearchResultAt(index); - } - - m2::PointD const & GetHotelReferencePoint() const { return m_hotels.m_viewportCenter; } - -private: - struct UISearchResults - { - m2::PointD m_viewportCenter; - search::Results m_results; - std::vector m_productInfos; - - void SetResults(search::Results const & res, - std::vector const & productInfo, - m2::PointD const & viewportCenter) - { - m_viewportCenter = viewportCenter; - m_results = res; - m_productInfos = productInfo; - } - - search::Result const & GetSearchResultAt(size_t const index) const - { - CHECK_LESS(index, m_results.GetCount(), ("Incorrect index:", index)); - return m_results[index]; - } - - search::ProductInfo const & GetProductInfoAt(size_t const index) const - { - CHECK_LESS(index, m_productInfos.size(), ("Incorrect index:", index)); - return m_productInfos[index]; - } - }; - - UISearchResults m_attractions; - UISearchResults m_cafes; - UISearchResults m_hotels; - std::vector m_experts; -}; -} // namespace discovery diff --git a/iphone/Maps/UI/Discovery/DiscoveryLocalExpertCell.swift b/iphone/Maps/UI/Discovery/DiscoveryLocalExpertCell.swift deleted file mode 100644 index a578fc4649..0000000000 --- a/iphone/Maps/UI/Discovery/DiscoveryLocalExpertCell.swift +++ /dev/null @@ -1,74 +0,0 @@ -@objc(MWMDiscoveryLocalExpertCell) -final class DiscoveryLocalExpertCell: UICollectionViewCell { - @IBOutlet private weak var avatar: UIImageView! - @IBOutlet private weak var name: UILabel! - @IBOutlet private weak var rating: RatingSummaryView! { - didSet { - rating.defaultConfig() - rating.textFont = UIFont.bold12() - rating.textSize = 12 - } - } - @IBOutlet private weak var price: UIButton! - - private lazy var formatter: NumberFormatter = { - let f = NumberFormatter() - f.numberStyle = .currency - return f - }() - - typealias Tap = () -> () - private var tap: Tap! - - override var isHighlighted: Bool { - didSet { - UIView.animate(withDuration: kDefaultAnimationDuration, - delay: 0, - options: [.allowUserInteraction, .beginFromCurrentState], - animations: { self.alpha = self.isHighlighted ? 0.3 : 1 }, - completion: nil) - } - } - - @objc func config(avatarURL: String, - name: String, - ratingValue: String, - ratingType: MWMRatingSummaryViewValueType, - price: Double, - currency: String, - tap: @escaping Tap) { - if avatarURL.count > 0, let url = URL(string: avatarURL) { - avatar.image = #imageLiteral(resourceName: "img_localsdefault") - avatar.wi_setImage(with: url, transitionDuration: kDefaultAnimationDuration) - } else { - avatar.image = #imageLiteral(resourceName: "img_localsdefault") - } - - self.name.text = name - rating.value = ratingValue - rating.type = ratingType - let str: String - if currency.count > 0, let cur = stringFor(price: price, currencyCode: currency) { - str = String(coreFormat: L("price_per_hour"), arguments:[cur]) - } else { - str = L("free") - } - - self.price.setTitle(str, for: .normal) - self.tap = tap - } - - @IBAction private func tapOnPrice() { - tap?() - } - - private func stringFor(price: Double, currencyCode: String) -> String? { - formatter.currencyCode = currencyCode - return formatter.string(for: price) - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - layer.borderColor = UIColor.blackDividers().cgColor - } -} diff --git a/iphone/Maps/UI/Discovery/DiscoveryLocalExpertCell.xib b/iphone/Maps/UI/Discovery/DiscoveryLocalExpertCell.xib deleted file mode 100644 index 14fa0484b5..0000000000 --- a/iphone/Maps/UI/Discovery/DiscoveryLocalExpertCell.xib +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/iphone/Maps/UI/Discovery/DiscoveryOnlineTemplateCell.swift b/iphone/Maps/UI/Discovery/DiscoveryOnlineTemplateCell.swift deleted file mode 100644 index a4b4d5e768..0000000000 --- a/iphone/Maps/UI/Discovery/DiscoveryOnlineTemplateCell.swift +++ /dev/null @@ -1,39 +0,0 @@ -@objc(MWMDiscoveryOnlineTemplateType) -enum DiscoveryOnlineTemplateType: Int { - case locals -} - -@objc(MWMDiscoveryOnlineTemplateCell) -final class DiscoveryOnlineTemplateCell: MWMTableViewCell { - @IBOutlet private weak var spinner: UIImageView! { - didSet { - let postfix = UIColor.isNightMode() ? "_dark" : "_light" - spinner.image = UIImage(named: "Spinner" + postfix) - } - } - - @IBOutlet private weak var title: UILabel! - @IBOutlet private weak var subtitle: UILabel! - - typealias Tap = () -> () - private var tap: Tap? - - @objc func config(type: DiscoveryOnlineTemplateType, needSpinner: Bool, tap: @escaping Tap) { - switch type { - case .locals: - title.text = needSpinner ? L("discovery_button_other_loading_message") : - L("discovery_button_other_error_message") - subtitle.text = "" - } - - spinner.isHidden = !needSpinner - if (needSpinner) { - spinner.startRotation() - } - self.tap = tap - } - - @IBAction private func onTap() { - tap?() - } -} diff --git a/iphone/Maps/UI/Discovery/DiscoveryOnlineTemplateCell.xib b/iphone/Maps/UI/Discovery/DiscoveryOnlineTemplateCell.xib deleted file mode 100644 index 69653253da..0000000000 --- a/iphone/Maps/UI/Discovery/DiscoveryOnlineTemplateCell.xib +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/iphone/Maps/UI/Discovery/MWMDiscoveryCityGalleryObjects.h b/iphone/Maps/UI/Discovery/MWMDiscoveryCityGalleryObjects.h new file mode 100644 index 0000000000..db4b225f12 --- /dev/null +++ b/iphone/Maps/UI/Discovery/MWMDiscoveryCityGalleryObjects.h @@ -0,0 +1,14 @@ +#include "partners_api/promo_api.hpp" + +NS_ASSUME_NONNULL_BEGIN + +@interface MWMDiscoveryCityGalleryObjects : NSObject + +- (instancetype)initWithGalleryResults:(promo::CityGallery const &)results; +- (promo::CityGallery::Item const &)galleryItemAtIndex:(NSUInteger)index; +- (NSUInteger)count; +- (nullable NSURL *)moreURL; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/Maps/UI/Discovery/MWMDiscoveryCityGalleryObjects.mm b/iphone/Maps/UI/Discovery/MWMDiscoveryCityGalleryObjects.mm new file mode 100644 index 0000000000..9d148feb33 --- /dev/null +++ b/iphone/Maps/UI/Discovery/MWMDiscoveryCityGalleryObjects.mm @@ -0,0 +1,35 @@ +#import "MWMDiscoveryCityGalleryObjects.h" + +@interface MWMDiscoveryCityGalleryObjects() { + promo::CityGallery m_results; +} +@end + +@implementation MWMDiscoveryCityGalleryObjects + +- (instancetype)initWithGalleryResults:(promo::CityGallery const &)results { + self = [super init]; + if (self) { + m_results = results; + } + return self; +} + +- (promo::CityGallery::Item const &)galleryItemAtIndex:(NSUInteger)index { + CHECK_LESS(index, m_results.m_items.size(), ("Incorrect index:", index)); + return m_results.m_items[index]; +} + +- (NSUInteger)count { + return m_results.m_items.size(); +} + +- (NSURL *)moreURL { + NSString *path = @(m_results.m_moreUrl.c_str()); + if (path != nil && path.length > 0) { + return [NSURL URLWithString:path]; + } + return nil; +} + +@end diff --git a/iphone/Maps/UI/Discovery/MWMDiscoveryController.mm b/iphone/Maps/UI/Discovery/MWMDiscoveryController.mm index 8e9e3ea3b9..12812d7ff6 100644 --- a/iphone/Maps/UI/Discovery/MWMDiscoveryController.mm +++ b/iphone/Maps/UI/Discovery/MWMDiscoveryController.mm @@ -1,6 +1,9 @@ #import "MWMDiscoveryController.h" #import "Framework.h" +#import "MWMDiscoveryControllerViewModel.h" +#import "MWMDiscoveryCityGalleryObjects.h" +#import "MWMDiscoveryMapObjects.h" #import "MWMDiscoveryTableManager.h" #import "MWMDiscoveryTapDelegate.h" #import "MWMEye.h" @@ -10,8 +13,7 @@ #import "MWMSearchManager+Filter.h" #import "Statistics.h" #import "UIKitCategories.h" - -#include "DiscoveryControllerViewModel.hpp" +#import "SwiftBridge.h" #include "map/discovery/discovery_client_params.hpp" #include "map/search_product_info.hpp" @@ -49,28 +51,28 @@ struct Callback m_setSearchResults(results, productInfo, viewportCenter, type); m_refreshSection(type); } - + void operator()(uint32_t const requestId, vector const & experts) const { - CHECK(m_setLocalExperts, ()); - CHECK(m_refreshSection, ()); - m_setLocalExperts(experts); - m_refreshSection(ItemType::LocalExperts); + // TODO: Please add correct implementation here. } void operator()(uint32_t const requestId, promo::CityGallery const & experts) const { - // TODO: Please add correct implementation here. + CHECK(m_setPromoCityGallery, ()); + CHECK(m_refreshSection, ()); + m_setPromoCityGallery(experts); + m_refreshSection(ItemType::Promo); } using SetSearchResults = function const & productInfo, m2::PointD const & viewportCenter, ItemType const type)>; - using SetLocalExperts = function const & experts)>; + using SetPromoCityGallery = function; using RefreshSection = function; SetSearchResults m_setSearchResults; - SetLocalExperts m_setLocalExperts; + SetPromoCityGallery m_setPromoCityGallery; RefreshSection m_refreshSection; }; } // namespace @@ -78,54 +80,67 @@ struct Callback @interface MWMDiscoveryController () { Callback m_callback; - DiscoveryControllerViewModel m_model; } @property(weak, nonatomic) IBOutlet UITableView * tableView; @property(nonatomic) MWMDiscoveryTableManager * tableManager; +@property(nonatomic) MWMDiscoveryControllerViewModel *viewModel; @property(nonatomic) BOOL canUseNetwork; @end @implementation MWMDiscoveryController -+ (instancetype)instanceWithConnection:(BOOL)canUseNetwork -{ ++ (instancetype)instanceWithConnection:(BOOL)canUseNetwork { auto instance = [[MWMDiscoveryController alloc] initWithNibName:self.className bundle:nil]; instance.title = L(@"discovery_button_title"); instance.canUseNetwork = canUseNetwork; return instance; } -- (instancetype)initWithNibName:(NSString *)name bundle:(NSBundle *)bundle -{ +- (instancetype)initWithNibName:(NSString *)name bundle:(NSBundle *)bundle { self = [super initWithNibName:name bundle:bundle]; - if (self) - { + if (self) { + _viewModel = [[MWMDiscoveryControllerViewModel alloc] init]; auto & cb = m_callback; - cb.m_setLocalExperts = bind(&DiscoveryControllerViewModel::SetExperts, &m_model, _1); - cb.m_setSearchResults = - bind(&DiscoveryControllerViewModel::SetSearchResults, &m_model, _1, _2, _3, _4); - cb.m_refreshSection = [self](ItemType const type) { [self.tableManager reloadItem:type]; }; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak" + __weak __typeof__(self) weakSelf = self; + cb.m_setSearchResults = [weakSelf](search::Results const & res, vector const & productInfo, m2::PointD const & viewportCenter, ItemType const type) { + __strong __typeof__(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { return; } + MWMDiscoveryMapObjects *objects = [[MWMDiscoveryMapObjects alloc] initWithSearchResults:res + productInfos:productInfo + viewPortCenter:viewportCenter]; + [strongSelf.viewModel updateMapObjects:objects forType:type]; + }; + cb.m_setPromoCityGallery = [weakSelf](promo::CityGallery const & experts) { + __strong __typeof__(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { return; } + MWMDiscoveryCityGalleryObjects *objects = [[MWMDiscoveryCityGalleryObjects alloc] initWithGalleryResults:experts]; + [strongSelf.viewModel updateCityGalleryObjects:objects]; + }; + cb.m_refreshSection = [weakSelf](ItemType const type) { + __strong __typeof__(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { return; } + [strongSelf.tableManager reloadItem:type]; + }; +#pragma clang diagnostic pop } return self; } -- (void)viewDidLoad -{ +- (void)viewDidLoad { [super viewDidLoad]; - auto callback = [self]() -> DiscoveryControllerViewModel const & { return self->m_model; }; - self.tableManager = [[MWMDiscoveryTableManager alloc] initWithTableView:self.tableView - delegate:self - model:move(callback)]; - - auto getTypes = [](BOOL canUseNetwork) -> vector { - if (canUseNetwork) - return {ItemType::Hotels, ItemType::Attractions, ItemType::Cafes, ItemType::LocalExperts}; - return {ItemType::Hotels, ItemType::Attractions, ItemType::Cafes}; + __weak __typeof__(self) weakSelf = self; + MWMGetModelCallback callback = ^{ + return weakSelf.viewModel; }; - - vector types = getTypes(self.canUseNetwork); + self.tableManager = [[MWMDiscoveryTableManager alloc] initWithTableView:self.tableView + canUseNetwork:self.canUseNetwork + delegate:self + model:callback]; + vector types = {ItemType::Hotels, ItemType::Attractions, ItemType::Cafes, ItemType:: Promo}; [self.tableManager loadItems:types]; ClientParams p; p.m_itemTypes = move(types); @@ -138,91 +153,90 @@ struct Callback #pragma mark - MWMDiscoveryTapDelegate -- (void)showSearchResult:(const search::Result &)item -{ +- (void)showSearchResult:(const search::Result &)item { GetFramework().ShowSearchResult(item); [self.navigationController popViewControllerAnimated:YES]; } -- (void)tapOnItem:(ItemType const)type atIndex:(size_t const)index -{ +- (void)tapOnItem:(ItemType const)type atIndex:(NSInteger)index { NSString * dest = @""; NSString * event = kStatPlacepageSponsoredItemSelected; MWMEyeDiscoveryEvent eyeEvent; - switch (type) - { - case ItemType::LocalExperts: - if (index == m_model.GetItemsCount(type)) - { - [self openURLForItem:type]; - event = kStatPlacepageSponsoredMoreSelected; - eyeEvent = MWMEyeDiscoveryEventMoreLocals; - } - else - { - [self openUrl:[NSURL URLWithString:@(m_model.GetExpertAt(index).m_pageUrl.c_str())]]; - eyeEvent = MWMEyeDiscoveryEventLocals; - } - dest = kStatExternal; - break; - case ItemType::Attractions: - if (index == m_model.GetItemsCount(type)) - { - [self searchTourism]; - eyeEvent = MWMEyeDiscoveryEventMoreAttractions; - } - else - { - [self showSearchResult:m_model.GetAttractionAt(index)]; - eyeEvent = MWMEyeDiscoveryEventAttractions; - } - - dest = kStatPlacePage; - break; - case ItemType::Cafes: - if (index == m_model.GetItemsCount(type)) - { - [self searchFood]; - eyeEvent = MWMEyeDiscoveryEventMoreCafes; - } - else - { - [self showSearchResult:m_model.GetCafeAt(index)]; - eyeEvent = MWMEyeDiscoveryEventCafes; - } - - dest = kStatPlacePage; - break; - case ItemType::Hotels: - if (index == m_model.GetItemsCount(type)) - { - [self openFilters]; - event = kStatPlacepageSponsoredMoreSelected; - dest = kStatSearchFilterOpen; - eyeEvent = MWMEyeDiscoveryEventMoreHotels; - } - else - { - [self showSearchResult:m_model.GetHotelAt(index)]; + switch (type) { + case ItemType::Attractions: + if (index == [self.viewModel itemsCountForType:type]) { + [self searchTourism]; + eyeEvent = MWMEyeDiscoveryEventMoreAttractions; + } else { + [self showSearchResult:[self.viewModel.attractions searchResultAtIndex:index]]; + eyeEvent = MWMEyeDiscoveryEventAttractions; + } + dest = kStatPlacePage; - eyeEvent = MWMEyeDiscoveryEventHotels; - } - break; + break; + case ItemType::Cafes: + if (index == [self.viewModel itemsCountForType:type]) { + [self searchFood]; + eyeEvent = MWMEyeDiscoveryEventMoreCafes; + } else { + [self showSearchResult:[self.viewModel.cafes searchResultAtIndex:index]]; + eyeEvent = MWMEyeDiscoveryEventCafes; + } + + dest = kStatPlacePage; + break; + case ItemType::Hotels: + if (index == [self.viewModel itemsCountForType:type]) { + [self openFilters]; + event = kStatPlacepageSponsoredMoreSelected; + dest = kStatSearchFilterOpen; + eyeEvent = MWMEyeDiscoveryEventMoreHotels; + } else { + [self showSearchResult:[self.viewModel.hotels searchResultAtIndex:index]]; + dest = kStatPlacePage; + eyeEvent = MWMEyeDiscoveryEventHotels; + } + break; + case ItemType::Promo: + if (index == [self.viewModel itemsCountForType:type]) { + [self logEvent:kStatPlacepageSponsoredMoreSelected + type:type + index:index + destination:kStatExternal]; + } else { + [self openURLForItem:type]; + [self logEvent:event + type:type + index:index + destination:kStatExternal]; + } + return; + case ItemType::LocalExperts: + return; } - - NSAssert(dest.length > 0, @""); - [Statistics logEvent:kStatPlacepageSponsoredItemSelected - withParameters:@{ - kStatProvider: StatProvider(type), - kStatPlacement: kStatDiscovery, - kStatItem: @(index + 1), - kStatDestination: dest - }]; + + [self logEvent:event + type:type + index:index + destination:dest]; [MWMEye discoveryItemClickedWithEvent:eyeEvent]; } -- (void)openFilters -{ +- (void)logEvent:(NSString *)eventName + type:(ItemType const)type + index:(NSInteger)index + destination:(NSString *)destination { + NSAssert(destination.length > 0, @""); + [Statistics logEvent:eventName + withParameters:@{ + kStatProvider: StatProvider(type), + kStatPlacement: kStatDiscovery, + kStatItem: @(index + 1), + kStatDestination: destination + }]; +} + +- (void)openFilters { [self.navigationController popViewControllerAnimated:YES]; [[MWMSearchManager manager] showHotelFilterWithParams:nil onFinishCallback:^{ @@ -232,22 +246,19 @@ struct Callback }]; } -- (void)searchFood -{ +- (void)searchFood { [self.navigationController popViewControllerAnimated:YES]; [MWMMapViewControlsManager.manager searchTextOnMap:[L(@"eat") stringByAppendingString:@" "] forInputLocale:[NSLocale currentLocale].localeIdentifier]; } -- (void)searchTourism -{ +- (void)searchTourism { [self.navigationController popViewControllerAnimated:YES]; [MWMMapViewControlsManager.manager searchTextOnMap:[L(@"tourism") stringByAppendingString:@" "] forInputLocale:[NSLocale currentLocale].localeIdentifier]; } -- (void)routeToItem:(ItemType const)type atIndex:(size_t const)index -{ +- (void)routeToItem:(ItemType const)type atIndex:(NSInteger)index { __block m2::PointD point; __block NSString * title; __block NSString * subtitle; @@ -262,16 +273,19 @@ struct Callback title = item.GetString().empty() ? subtitle : @(item.GetString().c_str()); }; - switch (type) - { - case ItemType::Attractions: getRoutePointInfo(m_model.GetAttractionAt(index)); break; - case ItemType::Cafes: getRoutePointInfo(m_model.GetCafeAt(index)); break; - case ItemType::Hotels: getRoutePointInfo(m_model.GetHotelAt(index)); break; - case ItemType::LocalExperts: - CHECK(false, ("Attempt to route to item with type:", static_cast(type))); - break; - // TODO: Add correct code here. - case ItemType::Promo: break; + switch (type) { + case ItemType::Attractions: + getRoutePointInfo([self.viewModel.attractions searchResultAtIndex:index]); + break; + case ItemType::Cafes: + getRoutePointInfo([self.viewModel.cafes searchResultAtIndex:index]); + break; + case ItemType::Hotels: + getRoutePointInfo([self.viewModel.hotels searchResultAtIndex:index]); + break; + case ItemType::Promo: + case ItemType::LocalExperts: + return; } MWMRoutePoint * pt = [[MWMRoutePoint alloc] initWithPoint:point @@ -283,22 +297,56 @@ struct Callback [MWMRouter buildToPoint:pt bestRouter:NO]; [self.navigationController popViewControllerAnimated:YES]; - [Statistics logEvent:kStatPlacepageSponsoredItemSelected - withParameters:@{ - kStatProvider: StatProvider(type), - kStatPlacement: kStatDiscovery, - kStatItem: @(index + 1), - kStatDestination: kStatRouting - }]; + [self logEvent:kStatPlacepageSponsoredItemSelected + type:type + index:index + destination:kStatRouting]; } -- (void)openURLForItem:(discovery::ItemType const)type -{ - CHECK(type == ItemType::LocalExperts, - ("Attempt to open url for item with type:", static_cast(type))); - auto & f = GetFramework(); - auto const url = f.GetDiscoveryLocalExpertsUrl(); - [self openUrl:[NSURL URLWithString:@(url.c_str())]]; +- (void)openURLForItem:(ItemType const)type atIndex:(NSInteger)index { + switch (type) { + case ItemType::Attractions: + case ItemType::Cafes: + case ItemType::Hotels: + case ItemType::LocalExperts: + break; + case ItemType::Promo: + auto bookmarks = [[MWMBookmarksTabViewController alloc] init]; + bookmarks.activeTab = ActiveTabCatalog; + promo::CityGallery::Item const &item = [self.viewModel.guides galleryItemAtIndex:index]; + NSString *itemPath = @(item.m_url.c_str()); + if (!itemPath || itemPath.length == 0) { + return; + } + MWMCatalogWebViewController *catalog = [[MWMCatalogWebViewController alloc] init:[NSURL URLWithString:itemPath]]; + NSMutableArray * controllers = [self.navigationController.viewControllers mutableCopy]; + [controllers addObjectsFromArray:@[bookmarks, catalog]]; + [self.navigationController setViewControllers:controllers animated:YES]; + [self logEvent:kStatPlacepageSponsoredItemSelected + type:type + index:index + destination:kStatExternal]; + break; + } +} + +- (void)openURLForItem:(ItemType const)type { + switch (type) { + case ItemType::Attractions: + case ItemType::Cafes: + case ItemType::Hotels: + case ItemType::LocalExperts: + break; + case ItemType::Promo: + auto bookmarks = [[MWMBookmarksTabViewController alloc] init]; + bookmarks.activeTab = ActiveTabCatalog; + NSURL *url = [self.viewModel.guides moreURL]; + MWMCatalogWebViewController *catalog = [[MWMCatalogWebViewController alloc] init:url]; + NSMutableArray * controllers = [self.navigationController.viewControllers mutableCopy]; + [controllers addObjectsFromArray:@[bookmarks, catalog]]; + [self.navigationController setViewControllers:controllers animated:YES]; + break; + } } @end diff --git a/iphone/Maps/UI/Discovery/MWMDiscoveryMapObjects.h b/iphone/Maps/UI/Discovery/MWMDiscoveryMapObjects.h new file mode 100644 index 0000000000..1c59649745 --- /dev/null +++ b/iphone/Maps/UI/Discovery/MWMDiscoveryMapObjects.h @@ -0,0 +1,23 @@ +#include "map/search_product_info.hpp" + +#include "search/result.hpp" + +#include "geometry/point2d.hpp" + +#include + +NS_ASSUME_NONNULL_BEGIN + +@interface MWMDiscoveryMapObjects : NSObject + +- (instancetype)initWithSearchResults:(search::Results const &)results + productInfos:(std::vector const &)productInfos + viewPortCenter:(m2::PointD const &)viewportCenter; +- (search::Result const &)searchResultAtIndex:(NSUInteger)index; +- (search::ProductInfo const &)productInfoAtIndex:(NSUInteger)index; +- (m2::PointD const &)viewPortCenter; +- (NSUInteger)count; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/Maps/UI/Discovery/MWMDiscoveryMapObjects.mm b/iphone/Maps/UI/Discovery/MWMDiscoveryMapObjects.mm new file mode 100644 index 0000000000..228dd830c6 --- /dev/null +++ b/iphone/Maps/UI/Discovery/MWMDiscoveryMapObjects.mm @@ -0,0 +1,43 @@ +#import "MWMDiscoveryMapObjects.h" + +@interface MWMDiscoveryMapObjects() { + search::Results m_results; + std::vector m_productInfos; + m2::PointD m_viewportCenter; +} + +@end + +@implementation MWMDiscoveryMapObjects + +- (instancetype)initWithSearchResults:(search::Results const &)results + productInfos:(std::vector const &)productInfos + viewPortCenter:(m2::PointD const &)viewportCenter { + self = [super init]; + if (self) { + m_results = results; + m_productInfos = productInfos; + m_viewportCenter = viewportCenter; + } + return self; +} + +- (search::Result const &)searchResultAtIndex:(NSUInteger)index { + CHECK_LESS(index, m_results.GetCount(), ("Incorrect index:", index)); + return m_results[index]; +} + +- (search::ProductInfo const &)productInfoAtIndex:(NSUInteger)index { + CHECK_LESS(index, m_productInfos.size(), ("Incorrect index:", index)); + return m_productInfos[index]; +} + +- (m2::PointD const &)viewPortCenter { + return m_viewportCenter; +} + +- (NSUInteger)count { + return m_results.GetCount(); +} + +@end diff --git a/iphone/Maps/UI/Discovery/MWMDiscoveryTableManager.h b/iphone/Maps/UI/Discovery/MWMDiscoveryTableManager.h index 72d3128bc6..28ea15172b 100644 --- a/iphone/Maps/UI/Discovery/MWMDiscoveryTableManager.h +++ b/iphone/Maps/UI/Discovery/MWMDiscoveryTableManager.h @@ -5,20 +5,21 @@ namespace discovery { -class DiscoveryControllerViewModel; - NSString * StatProvider(ItemType const type); } // namespace discovery -using GetModelCallback = std::function; +@class MWMDiscoveryControllerViewModel; + +typedef MWMDiscoveryControllerViewModel *(^MWMGetModelCallback)(void); @protocol MWMDiscoveryTapDelegate; @interface MWMDiscoveryTableManager : NSObject - (instancetype)initWithTableView:(UITableView *)tableView + canUseNetwork:(BOOL)canUseNetwork delegate:(id)delegate - model:(GetModelCallback &&)modelCallback; + model:(MWMGetModelCallback)modelCallback; - (void)loadItems:(std::vector const &)types; - (void)reloadItem:(discovery::ItemType const)type; diff --git a/iphone/Maps/UI/Discovery/MWMDiscoveryTableManager.mm b/iphone/Maps/UI/Discovery/MWMDiscoveryTableManager.mm index 10b520f222..ad5ef6f820 100644 --- a/iphone/Maps/UI/Discovery/MWMDiscoveryTableManager.mm +++ b/iphone/Maps/UI/Discovery/MWMDiscoveryTableManager.mm @@ -1,10 +1,12 @@ #import "MWMDiscoveryTableManager.h" #import "MWMDiscoveryTapDelegate.h" +#import "MWMDiscoveryControllerViewModel.h" +#import "MWMDiscoveryHotelViewModel.h" +#import "MWMDiscoverySearchViewModel.h" +#import "MWMDiscoveryGuideViewModel.h" #import "Statistics.h" #import "SwiftBridge.h" -#include "DiscoveryControllerViewModel.hpp" - #include "map/place_page_info.hpp" #include "partners_api/locals_api.hpp" @@ -32,41 +34,18 @@ namespace discovery { NSString * StatProvider(ItemType const type) { - switch (type) - { - case ItemType::LocalExperts: return kStatLocalsProvider; - case ItemType::Attractions: return kStatSearchAttractions; - case ItemType::Cafes: return kStatSearchRestaurants; - case ItemType::Hotels: return kStatBooking; - // TODO: Add correct stat key here. - case ItemType::Promo: return @""; + switch (type) { + case ItemType::Attractions: return kStatSearchAttractions; + case ItemType::Cafes: return kStatSearchRestaurants; + case ItemType::Hotels: return kStatBooking; + case ItemType::Promo: return kStatMapsmeGuides; + case ItemType::LocalExperts: return @""; } } - -pair FormattedRating(float const rawValue) -{ - auto const str = place_page::rating::GetRatingFormatted(rawValue); - auto const impress = static_cast(place_page::rating::GetImpress(rawValue)); - return {@(str.c_str()), impress}; -}; } // namespace discovery using namespace discovery; -namespace -{ -auto const kDefaultRowAnimation = UITableViewRowAnimationFade; - -string GetDistance(m2::PointD const & from, m2::PointD const & to) -{ - string distance; - auto const f = MercatorBounds::ToLatLon(from); - auto const t = MercatorBounds::ToLatLon(to); - measurement_utils::FormatDistance(ms::DistanceOnEarth(f.m_lat, f.m_lon, t.m_lat, t.m_lon), distance); - return distance; -} -} // namespace - @interface MWMDiscoveryCollectionView : UICollectionView @property(nonatomic) ItemType itemType; @end @@ -83,8 +62,9 @@ string GetDistance(m2::PointD const & from, m2::PointD const & to) } @property(weak, nonatomic) UITableView * tableView; -@property(nonatomic) GetModelCallback model; +@property(nonatomic) MWMGetModelCallback model; @property(weak, nonatomic) id delegate; +@property(nonatomic) BOOL canUseNetwork; @end @@ -93,15 +73,15 @@ string GetDistance(m2::PointD const & from, m2::PointD const & to) #pragma mark - Public - (instancetype)initWithTableView:(UITableView *)tableView + canUseNetwork:(BOOL)canUseNetwork delegate:(id)delegate - model:(GetModelCallback &&)modelCallback -{ + model:(MWMGetModelCallback)modelCallback { self = [super init]; - if (self) - { + if (self) { _tableView = tableView; _delegate = delegate; - _model = move(modelCallback); + _model = modelCallback; + _canUseNetwork = canUseNetwork; tableView.dataSource = self; tableView.rowHeight = UITableViewAutomaticDimension; tableView.estimatedRowHeight = 218; @@ -112,28 +92,29 @@ string GetDistance(m2::PointD const & from, m2::PointD const & to) return self; } -- (void)loadItems:(vector const &)types -{ +- (MWMDiscoveryControllerViewModel *)viewModel { + return self.model(); +} + +- (void)loadItems:(vector const &)types { m_types = types; m_loadingTypes = types; [self.tableView reloadData]; } -- (void)reloadItem:(ItemType const)type -{ - if (self.model().GetItemsCount(type) == 0) - { +- (void)reloadItem:(ItemType const)type { + if ([self.viewModel itemsCountForType:type] == 0) { [self removeItem:type]; return; } + - m_loadingTypes.erase(remove(m_loadingTypes.begin(), m_loadingTypes.end(), type), - m_loadingTypes.end()); - m_failedTypes.erase(remove(m_failedTypes.begin(), m_failedTypes.end(), type), - m_failedTypes.end()); - auto const position = [self position:type]; - [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:position]] - withRowAnimation:kDefaultRowAnimation]; + m_loadingTypes.erase(remove(m_loadingTypes.begin(), m_loadingTypes.end(), type), m_loadingTypes.end()); + m_failedTypes.erase(remove(m_failedTypes.begin(), m_failedTypes.end(), type), m_failedTypes.end()); + + NSInteger position = [self position:type]; + [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:position] + withRowAnimation:UITableViewRowAnimationFade]; [Statistics logEvent:kStatPlacepageSponsoredShow withParameters:@{ @@ -143,77 +124,68 @@ string GetDistance(m2::PointD const & from, m2::PointD const & to) }]; } -- (void)errorAtItem:(ItemType const)type -{ - CHECK(type == ItemType::LocalExperts, +- (void)errorAtItem:(ItemType const)type { + CHECK(type == ItemType::Promo, ("Error on item with type:", static_cast(type))); - m_loadingTypes.erase(remove(m_loadingTypes.begin(), m_loadingTypes.end(), type), - m_loadingTypes.end()); + m_loadingTypes.erase(remove(m_loadingTypes.begin(), m_loadingTypes.end(), type), m_loadingTypes.end()); m_failedTypes.push_back(type); - auto const position = [self position:type]; + NSInteger position = [self position:type]; [Statistics logEvent:kStatPlacepageSponsoredError - withParameters:@{kStatProvider: StatProvider(type), kStatPlacement: kStatDiscovery}]; + withParameters:@{kStatProvider: StatProvider(type), + kStatPlacement: kStatDiscovery}]; [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:position] - withRowAnimation:kDefaultRowAnimation]; + withRowAnimation:UITableViewRowAnimationFade]; } #pragma mark - Private -- (BOOL)hasOnlineSections -{ - return find(m_types.begin(), m_types.end(), ItemType::LocalExperts) != m_types.end(); +- (BOOL)hasOnlineSections { + return find(m_types.begin(), m_types.end(), ItemType::Promo) != m_types.end(); } -- (void)removeItem:(ItemType const)type -{ - auto const position = [self position:type]; +- (void)removeItem:(ItemType const)type { + NSInteger position = [self position:type]; m_types.erase(remove(m_types.begin(), m_types.end(), type), m_types.end()); - m_failedTypes.erase(remove(m_failedTypes.begin(), m_failedTypes.end(), type), - m_failedTypes.end()); - m_loadingTypes.erase(remove(m_loadingTypes.begin(), m_loadingTypes.end(), type), - m_loadingTypes.end()); + m_failedTypes.erase(remove(m_failedTypes.begin(), m_failedTypes.end(), type), m_failedTypes.end()); + m_loadingTypes.erase(remove(m_loadingTypes.begin(), m_loadingTypes.end(), type), m_loadingTypes.end()); auto indexSet = [NSIndexSet indexSetWithIndex:position]; auto tv = self.tableView; if (m_types.empty()) - [tv reloadSections:indexSet withRowAnimation:kDefaultRowAnimation]; + [tv reloadData]; else - [tv deleteSections:indexSet withRowAnimation:kDefaultRowAnimation]; + [tv deleteSections:indexSet withRowAnimation:UITableViewRowAnimationFade]; } -- (void)registerCells -{ +- (void)registerCells { auto tv = self.tableView; [tv registerWithCellClass:[MWMDiscoverySpinnerCell class]]; [tv registerWithCellClass:[MWMDiscoveryOnlineTemplateCell class]]; [tv registerWithCellClass:[MWMDiscoverySearchCollectionHolderCell class]]; - [tv registerWithCellClass:[MWMDiscoveryLocalExpertCollectionHolderCell class]]; + [tv registerWithCellClass:[MWMDiscoveryGuideCollectionHolderCell class]]; [tv registerWithCellClass:[MWMDiscoveryBookingCollectionHolderCell class]]; [tv registerWithCellClass:[MWMDiscoveryNoResultsCell class]]; } -- (NSInteger)position:(ItemType const)type -{ +- (NSInteger)position:(ItemType const)type { auto const it = find(m_types.begin(), m_types.end(), type); if (it == m_types.end()) CHECK(false, ("Incorrect item type:", static_cast(type))); - + return distance(m_types.begin(), it); } -- (MWMDiscoverySearchCollectionHolderCell *)searchCollectionHolderCell:(NSIndexPath *)indexPath -{ +- (MWMDiscoverySearchCollectionHolderCell *)searchCollectionHolderCell:(NSIndexPath *)indexPath { Class cls = [MWMDiscoverySearchCollectionHolderCell class]; - auto const type = m_types[indexPath.section]; - auto cell = static_cast( - [self.tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); - auto collection = static_cast(cell.collectionView); - switch (type) - { - case ItemType::Attractions: [cell configAttractionsCell]; break; - case ItemType::Cafes: [cell configCafesCell]; break; + ItemType const type = m_types[indexPath.section]; + MWMDiscoverySearchCollectionHolderCell *cell = (MWMDiscoverySearchCollectionHolderCell *) + [self.tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]; + MWMDiscoveryCollectionView *collection = (MWMDiscoveryCollectionView *)cell.collectionView; + switch (type) { + case ItemType::Attractions: [cell configAttractionsCell]; break; + case ItemType::Cafes: [cell configCafesCell]; break; default: NSAssert(false, @""); return nil; } collection.delegate = self; @@ -222,26 +194,11 @@ string GetDistance(m2::PointD const & from, m2::PointD const & to) return cell; } -- (MWMDiscoveryLocalExpertCollectionHolderCell *)localExpertsCollectionHolderCell: - (NSIndexPath *)indexPath -{ - Class cls = [MWMDiscoveryLocalExpertCollectionHolderCell class]; - auto cell = static_cast( - [self.tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); - auto collection = static_cast(cell.collectionView); - [cell config]; - collection.delegate = self; - collection.dataSource = self; - collection.itemType = ItemType::LocalExperts; - return cell; -} - -- (MWMDiscoveryBookingCollectionHolderCell *)bookingCollectionHolderCell:(NSIndexPath *)indexPath -{ +- (MWMDiscoveryBookingCollectionHolderCell *)bookingCollectionHolderCell:(NSIndexPath *)indexPath { Class cls = [MWMDiscoveryBookingCollectionHolderCell class]; - auto cell = static_cast( - [self.tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); - auto collection = static_cast(cell.collectionView); + MWMDiscoveryBookingCollectionHolderCell *cell = (MWMDiscoveryBookingCollectionHolderCell *) + [self.tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]; + MWMDiscoveryCollectionView *collection = (MWMDiscoveryCollectionView *)cell.collectionView; [cell config]; collection.delegate = self; collection.dataSource = self; @@ -249,69 +206,87 @@ string GetDistance(m2::PointD const & from, m2::PointD const & to) return cell; } +- (MWMDiscoveryGuideCollectionHolderCell *)guideCollectionHolderCell:(NSIndexPath *)indexPath { + Class cls = [MWMDiscoveryGuideCollectionHolderCell class]; + MWMDiscoveryGuideCollectionHolderCell *cell = (MWMDiscoveryGuideCollectionHolderCell *) + [self.tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]; + MWMDiscoveryCollectionView *collection = (MWMDiscoveryCollectionView *)cell.collectionView; + [cell config]; + collection.delegate = self; + collection.dataSource = self; + collection.itemType = ItemType::Promo; + return cell; +} + +- (MWMDiscoverySpinnerCell *)spinnerCell:(NSIndexPath *)indexPath { + Class cls = [MWMDiscoverySpinnerCell class]; + return (MWMDiscoverySpinnerCell *)[self.tableView dequeueReusableCellWithCellClass:cls + indexPath:indexPath]; +} + +- (MWMDiscoveryOnlineTemplateCell *)onlineTemplateCell:(NSIndexPath *)indexPath { + Class cls = [MWMDiscoveryOnlineTemplateCell class]; + return (MWMDiscoveryOnlineTemplateCell *)[self.tableView dequeueReusableCellWithCellClass:cls + indexPath:indexPath]; +} + #pragma mark - UITableViewDataSource -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - auto constexpr kNumberOfRows = 1; - return kNumberOfRows; +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return static_cast(MAX(m_types.size(), 1)); +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + if (m_types.empty()) { + return 1; + } + ItemType const type = m_types[section]; + switch (type) { + case ItemType::Attractions: + case ItemType::Cafes: + case ItemType::Hotels: + case ItemType::Promo: + return 1; + case ItemType::LocalExperts: + return 0; + } } - (UITableViewCell *)tableView:(UITableView *)tableView - cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - if (m_types.empty()) - { + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + if (m_types.empty()) { Class cls = [MWMDiscoveryNoResultsCell class]; - return static_cast( - [tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); + return (MWMDiscoveryNoResultsCell *)[tableView dequeueReusableCellWithCellClass:cls + indexPath:indexPath]; } + + ItemType const type = m_types[indexPath.section]; + BOOL isFailed = find(m_failedTypes.begin(), m_failedTypes.end(), type) != m_failedTypes.end(); + BOOL isLoading = find(m_loadingTypes.begin(), m_loadingTypes.end(), type) != m_loadingTypes.end(); - auto const type = m_types[indexPath.section]; - bool const isFailed = - find(m_failedTypes.begin(), m_failedTypes.end(), type) != m_failedTypes.end(); - bool const isLoading = - find(m_loadingTypes.begin(), m_loadingTypes.end(), type) != m_loadingTypes.end(); - - switch (type) - { - case ItemType::LocalExperts: - { - if (isLoading || isFailed) - { - Class cls = [MWMDiscoveryOnlineTemplateCell class]; - auto cell = static_cast( - [tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); - [cell configWithType:MWMDiscoveryOnlineTemplateTypeLocals - needSpinner:isLoading - tap:^{ - [self.delegate openURLForItem:type]; - }]; - return cell; + switch (type) { + case ItemType::Attractions: + case ItemType::Cafes: + return isLoading ? [self spinnerCell:indexPath] : [self searchCollectionHolderCell:indexPath]; + case ItemType::Hotels: + return isLoading ? [self spinnerCell:indexPath] : [self bookingCollectionHolderCell:indexPath]; + case ItemType::Promo: { + if (isLoading || isFailed) { + MWMDiscoveryOnlineTemplateCell *cell = [self onlineTemplateCell:indexPath]; + __weak __typeof__(self) weakSelf = self; + [cell configWithType:MWMDiscoveryOnlineTemplateTypePromo + needSpinner:isLoading + canUseNetwork: self.canUseNetwork + tap:^{ + [weakSelf.delegate openURLForItem:type]; + }]; + return cell; + } + return [self guideCollectionHolderCell: indexPath]; } - return [self localExpertsCollectionHolderCell:indexPath]; + case ItemType::LocalExperts: + return [[UITableViewCell alloc] init]; } - case ItemType::Attractions: - case ItemType::Cafes: - { - if (isLoading) - { - Class cls = [MWMDiscoverySpinnerCell class]; - auto cell = static_cast( - [tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); - return cell; - } - return [self searchCollectionHolderCell:indexPath]; - } - case ItemType::Hotels: return [self bookingCollectionHolderCell:indexPath]; - // TODO: Add correct value here. - case ItemType::Promo: return 0; - } -} - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return static_cast(MAX(m_types.size(), 1)); } #pragma mark - UICollectionViewDelegate @@ -327,137 +302,76 @@ string GetDistance(m2::PointD const & from, m2::PointD const & to) - (NSInteger)collectionView:(MWMDiscoveryCollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - auto const count = self.model().GetItemsCount(collectionView.itemType); + NSInteger count = [self.viewModel itemsCountForType:collectionView.itemType]; return count > 0 ? count + 1 : 0; } - (UICollectionViewCell *)collectionView:(MWMDiscoveryCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - auto const type = collectionView.itemType; - auto const & model = self.model(); - NSString * ratingValue; - MWMRatingSummaryViewValueType ratingType; - switch (type) - { - case ItemType::Attractions: - case ItemType::Cafes: - { - if (indexPath.row == model.GetItemsCount(type)) - { - Class cls = [MWMDiscoveryMoreCell class]; - auto cell = static_cast([collectionView - dequeueReusableCellWithCellClass:cls - indexPath:indexPath]); - return cell; - } - Class cls = [MWMDiscoverySearchCell class]; - auto cell = static_cast( - [collectionView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); - auto const & sr = type == ItemType::Attractions ? model.GetAttractionAt(indexPath.row) - : model.GetCafeAt(indexPath.row); - auto const & pt = type == ItemType::Attractions ? model.GetAttractionReferencePoint() - : model.GetCafeReferencePoint(); - auto const & pi = type == ItemType::Attractions ? model.GetAttractionProductInfoAt(indexPath.row) - : model.GetCafeProductInfoAt(indexPath.row); - tie(ratingValue, ratingType) = FormattedRating(pi.m_ugcRating); - - auto const readableType = classif().GetReadableObjectName(sr.GetFeatureType()); - auto const subtitle = platform::GetLocalizedTypeName(readableType); - - [cell configWithTitle:sr.GetString().empty() ? @(subtitle.c_str()) : @(sr.GetString().c_str()) - subtitle:@(subtitle.c_str()) - distance:@(GetDistance(pt, sr.GetFeatureCenter()).c_str()) - popular:sr.GetRankingInfo().m_popularity > 0 - ratingValue:ratingValue - ratingType:ratingType - tap:^{ - [self.delegate routeToItem:type atIndex:indexPath.row]; - }]; + ItemType const type = collectionView.itemType; + + if (indexPath.row == [self.viewModel itemsCountForType:type]) { + Class cls = [MWMDiscoveryMoreCell class]; + MWMDiscoveryMoreCell *cell = (MWMDiscoveryMoreCell *)[collectionView + dequeueReusableCellWithCellClass:cls + indexPath:indexPath]; return cell; } - case ItemType::LocalExperts: - { - if (indexPath.row == model.GetItemsCount(type)) - { - Class cls = [MWMDiscoveryMoreCell class]; - auto cell = static_cast([collectionView - dequeueReusableCellWithCellClass:cls - indexPath:indexPath]); + + switch (type) { + case ItemType::Attractions: + case ItemType::Cafes: { + Class cls = [MWMDiscoverySearchCell class]; + MWMDiscoverySearchCell *cell = (MWMDiscoverySearchCell *) + [collectionView dequeueReusableCellWithCellClass:cls indexPath:indexPath]; + MWMDiscoverySearchViewModel *objectVM = type == ItemType::Attractions ? [self.viewModel attractionAtIndex:indexPath.item] : [self.viewModel cafeAtIndex:indexPath.item]; + __weak __typeof__(self) weakSelf = self; + [cell configWithTitle:objectVM.title + subtitle:objectVM.subtitle + distance:objectVM.distance + popular:objectVM.isPopular + ratingValue:objectVM.ratingValue + ratingType:objectVM.ratingType + tap:^{ + [weakSelf.delegate routeToItem:type atIndex:indexPath.row]; + }]; return cell; } - - Class cls = [MWMDiscoveryLocalExpertCell class]; - auto cell = static_cast( - [collectionView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); - auto const & expert = model.GetExpertAt(indexPath.row); - tie(ratingValue, ratingType) = FormattedRating(expert.m_rating); - - [cell configWithAvatarURL:@(expert.m_photoUrl.c_str()) - name:@(expert.m_name.c_str()) - ratingValue:ratingValue - ratingType:ratingType - price:expert.m_pricePerHour - currency:@(expert.m_currency.c_str()) - tap:^{ - [self.delegate tapOnItem:type atIndex:indexPath.row]; + case ItemType::Hotels: { + Class cls = [MWMDiscoveryBookingCell class]; + MWMDiscoveryBookingCell *cell = (MWMDiscoveryBookingCell *) + [collectionView dequeueReusableCellWithCellClass:cls indexPath:indexPath]; + MWMDiscoveryHotelViewModel *objectVM = [self.viewModel hotelAtIndex:indexPath.item]; + __weak __typeof__(self) weakSelf = self; + [cell configWithAvatarURL:nil + title:objectVM.title + subtitle:objectVM.subtitle + price:objectVM.price + ratingValue:objectVM.ratingValue + ratingType:objectVM.ratingType + distance:objectVM.distance + onBuildRoute:^{ + [weakSelf.delegate routeToItem:type atIndex:indexPath.row]; + }]; + return cell; + } + case ItemType::Promo: { + Class cls = [MWMDiscoveryGuideCell class]; + MWMDiscoveryGuideCell *cell = (MWMDiscoveryGuideCell *) + [collectionView dequeueReusableCellWithCellClass:cls indexPath:indexPath]; + MWMDiscoveryGuideViewModel *objectVM = [self.viewModel guideAtIndex:indexPath.item]; + __weak __typeof__(self) weakSelf = self; + [cell configWithAvatarURL:objectVM.imagePath + title:objectVM.title + subtitle:objectVM.subtitle + label:objectVM.label onDetails:^{ + [weakSelf.delegate openURLForItem:ItemType::Promo atIndex:indexPath.row]; }]; - return cell; - } - case ItemType::Hotels: - { - if (indexPath.row == model.GetItemsCount(type)) - { - Class cls = [MWMDiscoveryMoreCell class]; - auto cell = static_cast([collectionView - dequeueReusableCellWithCellClass:cls - indexPath:indexPath]); return cell; } - - Class cls = [MWMDiscoveryBookingCell class]; - auto cell = static_cast( - [collectionView dequeueReusableCellWithCellClass:cls indexPath:indexPath]); - auto const & sr = model.GetHotelAt(indexPath.row); - auto const & pt = model.GetHotelReferencePoint(); - - NSMutableString * subtitle = nil; - auto starsCount = sr.GetStarsCount(); - if (starsCount == 0) - { - auto const readableType = classif().GetReadableObjectName(sr.GetFeatureType()); - subtitle = [@(platform::GetLocalizedTypeName(readableType).c_str()) mutableCopy]; - } - else - { - subtitle = [@"" mutableCopy]; - for (int i = 0; i < starsCount; ++i) - [subtitle appendString:@"★"]; - } - - tie(ratingValue, ratingType) = FormattedRating(sr.GetHotelRating()); - - [cell configWithAvatarURL:nil - title:sr.GetString().empty() ? [subtitle copy] : @(sr.GetString().c_str()) - subtitle:[subtitle copy] - price:@(sr.GetHotelApproximatePricing().c_str()) - ratingValue:ratingValue - ratingType:ratingType - distance:@(GetDistance(pt, sr.GetFeatureCenter()).c_str()) - onBuildRoute:^{ - [self.delegate routeToItem:type atIndex:indexPath.row]; - }]; - return cell; - } - case ItemType::Promo: - { - // TODO: Add correct implementation here. - auto cls = [MWMDiscoveryMoreCell class]; - auto cell = static_cast([collectionView - dequeueReusableCellWithCellClass:cls - indexPath:indexPath]); - return cell; - } + case ItemType::LocalExperts: + return [[UICollectionViewCell alloc] init]; } } diff --git a/iphone/Maps/UI/Discovery/MWMDiscoveryTapDelegate.h b/iphone/Maps/UI/Discovery/MWMDiscoveryTapDelegate.h index 422738d02b..719350fe98 100644 --- a/iphone/Maps/UI/Discovery/MWMDiscoveryTapDelegate.h +++ b/iphone/Maps/UI/Discovery/MWMDiscoveryTapDelegate.h @@ -2,8 +2,9 @@ @protocol MWMDiscoveryTapDelegate -- (void)tapOnItem:(discovery::ItemType const)type atIndex:(size_t const)index; -- (void)routeToItem:(discovery::ItemType const)type atIndex:(size_t const)index; +- (void)tapOnItem:(discovery::ItemType const)type atIndex:(NSInteger)index; +- (void)routeToItem:(discovery::ItemType const)type atIndex:(NSInteger)index; +- (void)openURLForItem:(discovery::ItemType const)type atIndex:(NSInteger)index; - (void)openURLForItem:(discovery::ItemType const)type; @end diff --git a/iphone/Maps/UI/Discovery/DiscoveryNoResultsCell.swift b/iphone/Maps/UI/Discovery/Table Cells/DiscoveryNoResultsCell.swift similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoveryNoResultsCell.swift rename to iphone/Maps/UI/Discovery/Table Cells/DiscoveryNoResultsCell.swift diff --git a/iphone/Maps/UI/Discovery/DiscoveryNoResultsCell.xib b/iphone/Maps/UI/Discovery/Table Cells/DiscoveryNoResultsCell.xib similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoveryNoResultsCell.xib rename to iphone/Maps/UI/Discovery/Table Cells/DiscoveryNoResultsCell.xib diff --git a/iphone/Maps/UI/Discovery/Table Cells/DiscoveryOnlineTemplateCell.swift b/iphone/Maps/UI/Discovery/Table Cells/DiscoveryOnlineTemplateCell.swift new file mode 100644 index 0000000000..5b888cc80f --- /dev/null +++ b/iphone/Maps/UI/Discovery/Table Cells/DiscoveryOnlineTemplateCell.swift @@ -0,0 +1,67 @@ +@objc(MWMDiscoveryOnlineTemplateType) +enum DiscoveryOnlineTemplateType: Int { + case promo +} + +@objc(MWMDiscoveryOnlineTemplateCell) +final class DiscoveryOnlineTemplateCell: MWMTableViewCell { + @IBOutlet var spinner: UIImageView! { + didSet { + let postfix = UIColor.isNightMode() ? "_dark" : "_light" + spinner.image = UIImage(named: "Spinner" + postfix) + spinner.startRotation() + } + } + + @IBOutlet var containerView: UIView! { + didSet { + containerView.layer.cornerRadius = 6.0 + containerView.layer.borderWidth = 1.0 + containerView.layer.borderColor = UIColor.blackDividers()?.cgColor + } + } + @IBOutlet var header: UILabel! + @IBOutlet var title: UILabel! + @IBOutlet var subtitle: UILabel! + @IBOutlet var actionButton: UIButton! + + typealias Tap = () -> () + private var tap: Tap? + + override func awakeFromNib() { + super.awakeFromNib() + backgroundColor = .clear + } + + override func prepareForReuse() { + super.prepareForReuse() + spinner.isHidden = true + spinner.stopRotation() + } + + @objc func config(type: DiscoveryOnlineTemplateType, needSpinner: Bool, canUseNetwork: Bool, tap: @escaping Tap) { + switch type { + case .promo: + header.text = L("gallery_pp_download_guides_title").uppercased() + title.text = L("gallery_pp_download_guides_offline_title") + subtitle.text = L("gallery_pp_download_guides_offline_subtitle") + if canUseNetwork { + actionButton.setTitle(L("details"), + for: .normal) + } else { + actionButton.setTitle(L("gallery_pp_download_guides_offline_cta"), + for: .normal) + } + } + + if (needSpinner) { + spinner.isHidden = false + spinner.startRotation() + } + self.tap = tap + } + + @IBAction private func onTap() { + tap?() + } +} diff --git a/iphone/Maps/UI/Discovery/Table Cells/DiscoveryOnlineTemplateCell.xib b/iphone/Maps/UI/Discovery/Table Cells/DiscoveryOnlineTemplateCell.xib new file mode 100644 index 0000000000..cb96e240db --- /dev/null +++ b/iphone/Maps/UI/Discovery/Table Cells/DiscoveryOnlineTemplateCell.xib @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/UI/Discovery/DiscoverySpinnerCell.swift b/iphone/Maps/UI/Discovery/Table Cells/DiscoverySpinnerCell.swift similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoverySpinnerCell.swift rename to iphone/Maps/UI/Discovery/Table Cells/DiscoverySpinnerCell.swift diff --git a/iphone/Maps/UI/Discovery/DiscoverySpinnerCell.xib b/iphone/Maps/UI/Discovery/Table Cells/DiscoverySpinnerCell.xib similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoverySpinnerCell.xib rename to iphone/Maps/UI/Discovery/Table Cells/DiscoverySpinnerCell.xib diff --git a/iphone/Maps/UI/Discovery/DiscoveryBookingCollectionHolderCell.xib b/iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoveryBookingCollectionHolderCell.xib similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoveryBookingCollectionHolderCell.xib rename to iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoveryBookingCollectionHolderCell.xib diff --git a/iphone/Maps/UI/Discovery/DiscoveryCollectionHolderCell.swift b/iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoveryCollectionHolderCell.swift similarity index 80% rename from iphone/Maps/UI/Discovery/DiscoveryCollectionHolderCell.swift rename to iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoveryCollectionHolderCell.swift index 2946b40547..3aeaa17a36 100644 --- a/iphone/Maps/UI/Discovery/DiscoveryCollectionHolderCell.swift +++ b/iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoveryCollectionHolderCell.swift @@ -3,15 +3,6 @@ class DiscoveryCollectionHolder: UITableViewCell { @IBOutlet fileprivate weak var header: UILabel! } -@objc(MWMDiscoveryLocalExpertCollectionHolderCell) -final class DiscoveryLocalExpertCollectionHolderCell: DiscoveryCollectionHolder { - @objc func config() { - header.text = L("discovery_button_subtitle_local_guides").uppercased() - collectionView.register(cellClass: DiscoveryLocalExpertCell.self) - collectionView.register(cellClass: DiscoveryMoreCell.self) - } -} - @objc(MWMDiscoverySearchCollectionHolderCell) final class DiscoverySearchCollectionHolderCell: DiscoveryCollectionHolder { @objc func configAttractionsCell() { @@ -37,3 +28,12 @@ final class DiscoveryBookingCollectionHolderCell: DiscoveryCollectionHolder { collectionView.register(cellClass: DiscoveryMoreCell.self) } } + +@objc(MWMDiscoveryGuideCollectionHolderCell) +final class DiscoveryGuideCollectionHolderCell: DiscoveryCollectionHolder { + @objc func config() { + header.text = L("discovery_button_subtitle_guides").uppercased() + collectionView.register(cellClass: DiscoveryGuideCell.self) + collectionView.register(cellClass: DiscoveryMoreCell.self) + } +} diff --git a/iphone/Maps/UI/Discovery/DiscoveryLocalExpertCollectionHolderCell.xib b/iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoveryGuideCollectionHolderCell.xib similarity index 55% rename from iphone/Maps/UI/Discovery/DiscoveryLocalExpertCollectionHolderCell.xib rename to iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoveryGuideCollectionHolderCell.xib index 04cd94b3cd..832998d35f 100644 --- a/iphone/Maps/UI/Discovery/DiscoveryLocalExpertCollectionHolderCell.xib +++ b/iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoveryGuideCollectionHolderCell.xib @@ -1,26 +1,26 @@ - - + + - - + + - - - - - + + + + + - - - - - - - - + + + + + + + - - - - - - + - - + + - + diff --git a/iphone/Maps/UI/Discovery/DiscoverySearchCollectionHolderCell.xib b/iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoverySearchCollectionHolderCell.xib similarity index 100% rename from iphone/Maps/UI/Discovery/DiscoverySearchCollectionHolderCell.xib rename to iphone/Maps/UI/Discovery/Table Cells/Holders/DiscoverySearchCollectionHolderCell.xib diff --git a/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryControllerViewModel.h b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryControllerViewModel.h new file mode 100644 index 0000000000..3805766c14 --- /dev/null +++ b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryControllerViewModel.h @@ -0,0 +1,30 @@ +#include "map/discovery/discovery_client_params.hpp" + +NS_ASSUME_NONNULL_BEGIN + +@class MWMDiscoveryMapObjects; +@class MWMDiscoveryCityGalleryObjects; +@class MWMDiscoverySearchViewModel; +@class MWMDiscoveryHotelViewModel; +@class MWMDiscoveryGuideViewModel; + +@interface MWMDiscoveryControllerViewModel : NSObject + +@property(nonatomic, readonly) MWMDiscoveryMapObjects *attractions; +@property(nonatomic, readonly) MWMDiscoveryMapObjects *cafes; +@property(nonatomic, readonly) MWMDiscoveryMapObjects *hotels; +@property(nonatomic, readonly) MWMDiscoveryCityGalleryObjects *guides; + +- (void)updateMapObjects:(MWMDiscoveryMapObjects *)objects + forType:(discovery::ItemType const)type; +- (void)updateCityGalleryObjects:(MWMDiscoveryCityGalleryObjects *)objects; +- (NSUInteger)itemsCountForType:(discovery::ItemType const)type; + +- (MWMDiscoverySearchViewModel *)attractionAtIndex:(NSUInteger)index; +- (MWMDiscoverySearchViewModel *)cafeAtIndex:(NSUInteger)index; +- (MWMDiscoveryHotelViewModel *)hotelAtIndex:(NSUInteger)index; +- (MWMDiscoveryGuideViewModel *)guideAtIndex:(NSUInteger)index; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryControllerViewModel.mm b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryControllerViewModel.mm new file mode 100644 index 0000000000..4975debcdd --- /dev/null +++ b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryControllerViewModel.mm @@ -0,0 +1,184 @@ +#import "MWMDiscoveryControllerViewModel.h" +#import "MWMDiscoveryCityGalleryObjects.h" +#import "MWMDiscoveryMapObjects.h" +#import "MWMDiscoveryHotelViewModel.h" +#import "MWMDiscoverySearchViewModel.h" +#import "MWMDiscoveryGuideViewModel.h" +#import "MWMRatingSummaryViewValueType.h" + +#include "map/place_page_info.hpp" + +#include "platform/localization.hpp" +#include "platform/measurement_utils.hpp" + +#include "geometry/distance_on_sphere.hpp" +#include "geometry/mercator.hpp" + +using namespace discovery; + +@interface MWMDiscoveryControllerViewModel() + +@property(nonatomic, readwrite) MWMDiscoveryMapObjects *attractions; +@property(nonatomic, readwrite) MWMDiscoveryMapObjects *cafes; +@property(nonatomic, readwrite) MWMDiscoveryMapObjects *hotels; +@property(nonatomic, readwrite) MWMDiscoveryCityGalleryObjects *guides; + +@end + +@implementation MWMDiscoveryControllerViewModel + +- (void)updateMapObjects:(MWMDiscoveryMapObjects *)objects + forType:(ItemType const)type { + switch (type) { + case ItemType::Attractions: + self.attractions = objects; + break; + case ItemType::Cafes: + self.cafes = objects; + break; + case ItemType::Hotels: + self.hotels = objects; + break; + case ItemType::Promo: + break; + case ItemType::LocalExperts: + break; + } +} + +- (void)updateCityGalleryObjects:(MWMDiscoveryCityGalleryObjects *)objects { + self.guides = objects; +} + +- (NSUInteger)itemsCountForType:(ItemType const)type { + switch (type) { + case ItemType::Attractions: + return self.attractions.count; + case ItemType::Cafes: + return self.cafes.count; + case ItemType::Hotels: + return self.hotels.count; + case ItemType::Promo: + return self.guides.count; + case ItemType::LocalExperts: + return 0; + } +} + +- (MWMDiscoverySearchViewModel *)attractionAtIndex:(NSUInteger)index { + search::Result const &result = [self.attractions searchResultAtIndex:index]; + search::ProductInfo const &info = [self.attractions productInfoAtIndex:index]; + m2::PointD const ¢er = [self.attractions viewPortCenter]; + return [self searchViewModelForResult:result + productInfo:info + viewPortCenter:center]; +} + +- (MWMDiscoverySearchViewModel *)cafeAtIndex:(NSUInteger)index { + search::Result const &result = [self.cafes searchResultAtIndex:index]; + search::ProductInfo const &info = [self.cafes productInfoAtIndex:index]; + m2::PointD const ¢er = [self.cafes viewPortCenter]; + return [self searchViewModelForResult:result + productInfo:info + viewPortCenter:center]; +} + +- (MWMDiscoveryHotelViewModel *)hotelAtIndex:(NSUInteger)index { + search::Result const &result = [self.hotels searchResultAtIndex:index]; + search::ProductInfo const &info = [self.hotels productInfoAtIndex:index]; + m2::PointD const ¢er = [self.hotels viewPortCenter]; + return [self hotelViewModelForResult:result + productInfo:info + viewPortCenter:center]; +} + +- (MWMDiscoveryGuideViewModel *)guideAtIndex:(NSUInteger)index { + promo::CityGallery::Item const &item = [self.guides galleryItemAtIndex:index]; + return [self guideViewModelForItem:item]; +} + +#pragma mark - Builders + +- (MWMDiscoverySearchViewModel *)searchViewModelForResult:(search::Result const &)result + productInfo:(search::ProductInfo const &)info + viewPortCenter:(m2::PointD const &)center { + + auto const readableType = classif().GetReadableObjectName(result.GetFeatureType()); + NSString *subtitle = @(platform::GetLocalizedTypeName(readableType).c_str()); + NSString *title = result.GetString().empty() ? subtitle : @(result.GetString().c_str()); + + NSString *ratingValue = [self ratingValueForProductInfo:info]; + MWMRatingSummaryViewValueType ratingType = [self ratingTypeForProductInfo:info]; + + NSString *distance = [self distanceFrom:center + to:result.GetFeatureCenter()]; + + BOOL isPopular = result.GetRankingInfo().m_popularity > 0; + + return [[MWMDiscoverySearchViewModel alloc] initWithTitle:title + subtitle:subtitle + distance:distance + isPopular:isPopular + ratingValue:ratingValue + ratingType:ratingType]; +} + +- (MWMDiscoveryHotelViewModel *)hotelViewModelForResult:(search::Result const &)result + productInfo:(search::ProductInfo const &)info + viewPortCenter:(m2::PointD const &)center { + + auto const readableType = classif().GetReadableObjectName(result.GetFeatureType()); + NSString *subtitle = @(platform::GetLocalizedTypeName(readableType).c_str()); + NSString *title = result.GetString().empty() ? subtitle : @(result.GetString().c_str()); + NSUInteger starsCount = result.GetStarsCount(); + if (starsCount > 0) { + subtitle = [@"" stringByPaddingToLength:starsCount + withString:@"★" + startingAtIndex:0]; + } + NSString *price = @(result.GetHotelApproximatePricing().c_str()); + + NSString *ratingValue = [self ratingValueForProductInfo:info]; + MWMRatingSummaryViewValueType ratingType = [self ratingTypeForProductInfo:info]; + + NSString *distance = [self distanceFrom:center + to:result.GetFeatureCenter()]; + + BOOL isPopular = result.GetRankingInfo().m_popularity > 0; + + return [[MWMDiscoveryHotelViewModel alloc] initWithTitle:title + subtitle:subtitle + price:price + distance:distance + isPopular:isPopular + ratingValue:ratingValue + ratingType:ratingType]; +} + +- (MWMDiscoveryGuideViewModel *)guideViewModelForItem:(promo::CityGallery::Item const &)item { + return [[MWMDiscoveryGuideViewModel alloc] initWithTitle:@(item.m_name.c_str()) + subtitle:@(item.m_author.m_name.c_str()) + label:@(item.m_luxCategory.m_name.c_str()) + imageURL:@(item.m_imageUrl.c_str())]; +} + +#pragma mark - Helpers + +- (NSString *)distanceFrom:(m2::PointD const &)startPoint + to:(m2::PointD const &)endPoint { + string distance; + auto const f = MercatorBounds::ToLatLon(startPoint); + auto const t = MercatorBounds::ToLatLon(endPoint); + measurement_utils::FormatDistance(ms::DistanceOnEarth(f.m_lat, f.m_lon, t.m_lat, t.m_lon), distance); + return @(distance.c_str()); +} + +- (NSString *)ratingValueForProductInfo:(search::ProductInfo const &)info { + return @(place_page::rating::GetRatingFormatted(info.m_ugcRating).c_str()); +} + +- (MWMRatingSummaryViewValueType)ratingTypeForProductInfo:(search::ProductInfo const &)info { + return (MWMRatingSummaryViewValueType)place_page::rating::GetImpress(info.m_ugcRating); +} + +@end diff --git a/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryGuideViewModel.h b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryGuideViewModel.h new file mode 100644 index 0000000000..cb5be67618 --- /dev/null +++ b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryGuideViewModel.h @@ -0,0 +1,17 @@ +NS_ASSUME_NONNULL_BEGIN + +@interface MWMDiscoveryGuideViewModel : NSObject + +@property(nonatomic, readonly) NSString *title; +@property(nonatomic, readonly) NSString *subtitle; +@property(nonatomic, nullable, readonly) NSString *label; +@property(nonatomic, nullable, readonly) NSString *imagePath; + +- (instancetype)initWithTitle:(NSString *)title + subtitle:(NSString *)subtitle + label:(nullable NSString *)label + imageURL:(nullable NSString *) imagePath; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryGuideViewModel.m b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryGuideViewModel.m new file mode 100644 index 0000000000..18cd51683d --- /dev/null +++ b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryGuideViewModel.m @@ -0,0 +1,28 @@ +#import "MWMDiscoveryGuideViewModel.h" + +@interface MWMDiscoveryGuideViewModel() + +@property(nonatomic, readwrite) NSString *title; +@property(nonatomic, readwrite) NSString *subtitle; +@property(nonatomic, readwrite) NSString *label; +@property(nonatomic, readwrite) NSString *imagePath; + +@end + +@implementation MWMDiscoveryGuideViewModel + +- (instancetype)initWithTitle:(NSString *)title + subtitle:(NSString *)subtitle + label:(NSString *)label + imageURL:(NSString *) imagePath { + self = [super init]; + if (self) { + self.title = title; + self.subtitle = subtitle; + self.label = label; + self.imagePath = imagePath; + } + return self; +} + +@end diff --git a/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryHotelViewModel.h b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryHotelViewModel.h new file mode 100644 index 0000000000..150f5204b0 --- /dev/null +++ b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryHotelViewModel.h @@ -0,0 +1,25 @@ +#import "MWMRatingSummaryViewValueType.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MWMDiscoveryHotelViewModel : NSObject + +@property(nonatomic, readonly) NSString *title; +@property(nonatomic, readonly) NSString *subtitle; +@property(nonatomic, readonly) NSString *price; +@property(nonatomic, readonly) NSString *distance; +@property(nonatomic, readonly) BOOL isPopular; +@property(nonatomic, readonly) NSString *ratingValue; +@property(nonatomic, readonly) MWMRatingSummaryViewValueType ratingType; + +- (instancetype)initWithTitle:(NSString *)title + subtitle:(NSString *)subtitle + price:(NSString *)price + distance:(NSString *)distance + isPopular:(BOOL)isPopular + ratingValue:(NSString *) ratingValue + ratingType:(MWMRatingSummaryViewValueType)ratingType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryHotelViewModel.m b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryHotelViewModel.m new file mode 100644 index 0000000000..5a30eeaf73 --- /dev/null +++ b/iphone/Maps/UI/Discovery/View Models/MWMDiscoveryHotelViewModel.m @@ -0,0 +1,37 @@ +#import "MWMDiscoveryHotelViewModel.h" + +@interface MWMDiscoveryHotelViewModel() + +@property(nonatomic, readwrite) NSString *title; +@property(nonatomic, readwrite) NSString *subtitle; +@property(nonatomic, readwrite) NSString *price; +@property(nonatomic, readwrite) NSString *distance; +@property(nonatomic, readwrite) BOOL isPopular; +@property(nonatomic, readwrite) NSString *ratingValue; +@property(nonatomic, readwrite) MWMRatingSummaryViewValueType ratingType; + +@end + +@implementation MWMDiscoveryHotelViewModel + +- (instancetype)initWithTitle:(NSString *)title + subtitle:(NSString *)subtitle + price:(NSString *)price + distance:(NSString *)distance + isPopular:(BOOL)isPopular + ratingValue:(NSString *) ratingValue + ratingType:(MWMRatingSummaryViewValueType)ratingType { + self = [super init]; + if (self) { + self.title = title; + self.subtitle = subtitle; + self.price = price; + self.distance = distance; + self.isPopular = isPopular; + self.ratingValue = ratingValue; + self.ratingType = ratingType; + } + return self; +} + +@end diff --git a/iphone/Maps/UI/Discovery/View Models/MWMDiscoverySearchViewModel.h b/iphone/Maps/UI/Discovery/View Models/MWMDiscoverySearchViewModel.h new file mode 100644 index 0000000000..48e46b19e1 --- /dev/null +++ b/iphone/Maps/UI/Discovery/View Models/MWMDiscoverySearchViewModel.h @@ -0,0 +1,22 @@ +#import "MWMRatingSummaryViewValueType.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MWMDiscoverySearchViewModel : NSObject + +@property(nonatomic, readonly) NSString *title; +@property(nonatomic, readonly) NSString *subtitle; +@property(nonatomic, readonly) NSString *distance; +@property(nonatomic, readonly) BOOL isPopular; +@property(nonatomic, readonly) NSString *ratingValue; +@property(nonatomic, readonly) MWMRatingSummaryViewValueType ratingType; + +- (instancetype)initWithTitle:(NSString *)title + subtitle:(NSString *)subtitle + distance:(NSString *)distance + isPopular:(BOOL)isPopular + ratingValue:(NSString *) ratingValue + ratingType:(MWMRatingSummaryViewValueType)ratingType; +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/Maps/UI/Discovery/View Models/MWMDiscoverySearchViewModel.m b/iphone/Maps/UI/Discovery/View Models/MWMDiscoverySearchViewModel.m new file mode 100644 index 0000000000..399d11d8fb --- /dev/null +++ b/iphone/Maps/UI/Discovery/View Models/MWMDiscoverySearchViewModel.m @@ -0,0 +1,34 @@ +#import "MWMDiscoverySearchViewModel.h" + +@interface MWMDiscoverySearchViewModel() + +@property(nonatomic, readwrite) NSString *title; +@property(nonatomic, readwrite) NSString *subtitle; +@property(nonatomic, readwrite) NSString *distance; +@property(nonatomic, readwrite) BOOL isPopular; +@property(nonatomic, readwrite) NSString *ratingValue; +@property(nonatomic, readwrite) MWMRatingSummaryViewValueType ratingType; + +@end + +@implementation MWMDiscoverySearchViewModel + +- (instancetype)initWithTitle:(NSString *)title + subtitle:(NSString *)subtitle + distance:(NSString *)distance + isPopular:(BOOL)isPopular + ratingValue:(NSString *) ratingValue + ratingType:(MWMRatingSummaryViewValueType)ratingType { + self = [super init]; + if (self) { + self.title = title; + self.subtitle = subtitle; + self.distance = distance; + self.isPopular = isPopular; + self.ratingValue = ratingValue; + self.ratingType = ratingType; + } + return self; +} + +@end