[iOS][Cross-Traffic] Guides on the discovery screen

This commit is contained in:
Zoia Pribytkova 2019-05-29 16:17:44 +03:00 committed by Aleksey Belousov
parent 48e472dd71
commit 7889a2a982
43 changed files with 1419 additions and 952 deletions

View file

@ -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";

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "Illustration.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -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 = "<group>"; };
BB8123D52130427E00ADE512 /* MetalContextFactory.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MetalContextFactory.mm; sourceTree = "<group>"; };
CD96C70A22A681C400DB7CFE /* DiscoveryGuideCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryGuideCell.swift; sourceTree = "<group>"; };
CD96C70B22A681C400DB7CFE /* DiscoveryGuideCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryGuideCell.xib; sourceTree = "<group>"; };
CD96C70F22A6820800DB7CFE /* DiscoveryGuideCollectionHolderCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryGuideCollectionHolderCell.xib; sourceTree = "<group>"; };
CD96C71222A6CBFD00DB7CFE /* MWMDiscoveryGuideViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryGuideViewModel.h; sourceTree = "<group>"; };
CD96C71322A6CBFD00DB7CFE /* MWMDiscoveryGuideViewModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMDiscoveryGuideViewModel.m; sourceTree = "<group>"; };
CD96C71522A7B5DE00DB7CFE /* MWMDiscoveryCityGalleryObjects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryCityGalleryObjects.h; sourceTree = "<group>"; };
CD96C71622A7B5DE00DB7CFE /* MWMDiscoveryCityGalleryObjects.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMDiscoveryCityGalleryObjects.mm; sourceTree = "<group>"; };
CDB92CE9229E9ADF00EC757C /* MWMDiscoveryControllerViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryControllerViewModel.h; sourceTree = "<group>"; };
CDB92CEA229E9ADF00EC757C /* MWMDiscoveryControllerViewModel.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMDiscoveryControllerViewModel.mm; sourceTree = "<group>"; };
CDB92CEC229E9CF900EC757C /* MWMDiscoveryMapObjects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryMapObjects.h; sourceTree = "<group>"; };
CDB92CED229E9CF900EC757C /* MWMDiscoveryMapObjects.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMDiscoveryMapObjects.mm; sourceTree = "<group>"; };
CDB92CEF229EB8A700EC757C /* MWMDiscoverySearchViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoverySearchViewModel.h; sourceTree = "<group>"; };
CDB92CF0229EB8A800EC757C /* MWMDiscoverySearchViewModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMDiscoverySearchViewModel.m; sourceTree = "<group>"; };
CDB92CF622A5350500EC757C /* MWMDiscoveryHotelViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryHotelViewModel.h; sourceTree = "<group>"; };
CDB92CF722A5350500EC757C /* MWMDiscoveryHotelViewModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMDiscoveryHotelViewModel.m; sourceTree = "<group>"; };
ED48BBB317C267F5003E7E92 /* ColorPickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorPickerView.h; sourceTree = "<group>"; };
ED48BBB417C267F5003E7E92 /* ColorPickerView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ColorPickerView.mm; sourceTree = "<group>"; };
ED48BBB817C2B1E2003E7E92 /* CircleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircleView.h; sourceTree = "<group>"; };
@ -1615,7 +1636,6 @@
F5BD246A7E6BE8CD8600EDD9 /* DiscoverySpinnerCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscoverySpinnerCell.swift; sourceTree = "<group>"; };
F5BD2A86D9DA2F9769D30B54 /* DiscoveryCollectionHolderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscoveryCollectionHolderCell.swift; sourceTree = "<group>"; };
F5BD2ED6E94925472A9901B4 /* DiscoverySearchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscoverySearchCell.swift; sourceTree = "<group>"; };
F603E0581FDE9410006B84D6 /* DiscoveryLocalExpertCollectionHolderCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryLocalExpertCollectionHolderCell.xib; sourceTree = "<group>"; };
F603E05C1FDE9703006B84D6 /* DiscoverySearchCollectionHolderCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoverySearchCollectionHolderCell.xib; sourceTree = "<group>"; };
F607C1831C032A8800B53A87 /* resources-hdpi_clear */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-hdpi_clear"; path = "../../data/resources-hdpi_clear"; sourceTree = "<group>"; };
F607C1841C032A8800B53A87 /* resources-hdpi_dark */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-hdpi_dark"; path = "../../data/resources-hdpi_dark"; sourceTree = "<group>"; };
@ -1695,10 +1715,7 @@
F69018BB1E9F7CB600B3C10B /* MWMAutoupdateController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMAutoupdateController.xib; sourceTree = "<group>"; };
F69739AF1FD197DB00FDA07D /* MWMDiscoveryTableManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryTableManager.h; sourceTree = "<group>"; };
F69739B01FD197DB00FDA07D /* MWMDiscoveryTableManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMDiscoveryTableManager.mm; sourceTree = "<group>"; };
F69739B41FD198E300FDA07D /* DiscoveryControllerViewModel.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiscoveryControllerViewModel.hpp; sourceTree = "<group>"; };
F69739B51FD19D9900FDA07D /* MWMDiscoveryTapDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDiscoveryTapDelegate.h; sourceTree = "<group>"; };
F69739DA1FD6ECCE00FDA07D /* DiscoveryLocalExpertCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryLocalExpertCell.swift; sourceTree = "<group>"; };
F69739DE1FD6EE1D00FDA07D /* DiscoveryLocalExpertCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryLocalExpertCell.xib; sourceTree = "<group>"; };
F69CE8D41E5C49B4002B5881 /* PPHotelCarouselCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PPHotelCarouselCell.swift; sourceTree = "<group>"; };
F69CE8D81E5C5088002B5881 /* PPHotelCarouselCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PPHotelCarouselCell.xib; sourceTree = "<group>"; };
F69CE8DC1E5C51AB002B5881 /* CarouselElement.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CarouselElement.xib; sourceTree = "<group>"; };
@ -3512,6 +3529,61 @@
path = Catalog;
sourceTree = "<group>";
};
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 = "<group>";
};
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 = "<group>";
};
CD96C71A22A80D1600DB7CFE /* Holders */ = {
isa = PBXGroup;
children = (
CD96C70F22A6820800DB7CFE /* DiscoveryGuideCollectionHolderCell.xib */,
34B6FD612015F71900C18E97 /* DiscoveryBookingCollectionHolderCell.xib */,
F603E05C1FDE9703006B84D6 /* DiscoverySearchCollectionHolderCell.xib */,
F5BD2A86D9DA2F9769D30B54 /* DiscoveryCollectionHolderCell.swift */,
);
path = Holders;
sourceTree = "<group>";
};
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 = "<group>";
};
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 = "<group>";
@ -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 */,

View file

@ -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?()
}
}

View file

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="DiscoveryGuideCell" id="gTV-IL-0wX" customClass="MWMDiscoveryGuideCell">
<rect key="frame" x="0.0" y="0.0" width="160" height="194"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="160" height="194"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="PVI-wd-HVI">
<rect key="frame" x="0.0" y="0.0" width="160" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="kmH-tC-wJm"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yJV-Vb-Cna">
<rect key="frame" x="0.0" y="154" width="160" height="40"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.040000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="160" id="k85-sb-f1d"/>
<constraint firstAttribute="height" constant="40" id="mzG-ku-CVD"/>
</constraints>
<state key="normal" title="Button"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="textColorName" value="linkBlue"/>
</userDefinedRuntimeAttributes>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7eA-bJ-coY">
<rect key="frame" x="12" y="94" width="136" height="21"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fEh-1z-Iwj">
<rect key="frame" x="12" y="119" width="136" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="C9b-m0-Yo8">
<rect key="frame" x="0.0" y="46" width="54" height="24"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="PRO" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vHm-DF-mQY">
<rect key="frame" x="12" y="2" width="34" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="vHm-DF-mQY" secondAttribute="trailing" constant="8" id="PrH-Je-n6I"/>
<constraint firstItem="vHm-DF-mQY" firstAttribute="centerY" secondItem="C9b-m0-Yo8" secondAttribute="centerY" id="Xs2-If-e7l"/>
<constraint firstItem="vHm-DF-mQY" firstAttribute="leading" secondItem="C9b-m0-Yo8" secondAttribute="leading" constant="12" id="Y5o-2R-3Oc"/>
<constraint firstAttribute="height" constant="24" id="wua-lE-6XL"/>
</constraints>
</view>
</subviews>
</view>
<constraints>
<constraint firstItem="fEh-1z-Iwj" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="12" id="0by-Hl-DH6"/>
<constraint firstItem="C9b-m0-Yo8" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="46" id="4kb-GH-H1e"/>
<constraint firstAttribute="trailing" secondItem="PVI-wd-HVI" secondAttribute="trailing" id="8Cm-hB-mok"/>
<constraint firstItem="PVI-wd-HVI" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="B20-vK-ARj"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="C9b-m0-Yo8" secondAttribute="trailing" constant="12" id="C8r-A9-Der"/>
<constraint firstItem="fEh-1z-Iwj" firstAttribute="top" secondItem="7eA-bJ-coY" secondAttribute="bottom" constant="4" id="LiX-Yq-174"/>
<constraint firstAttribute="trailing" secondItem="fEh-1z-Iwj" secondAttribute="trailing" constant="12" id="Umy-G4-a7D"/>
<constraint firstAttribute="bottom" secondItem="yJV-Vb-Cna" secondAttribute="bottom" id="XF5-Eb-seh"/>
<constraint firstAttribute="trailing" secondItem="yJV-Vb-Cna" secondAttribute="trailing" id="aDP-NV-THf"/>
<constraint firstItem="C9b-m0-Yo8" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="bfT-M4-gwc"/>
<constraint firstItem="7eA-bJ-coY" firstAttribute="top" secondItem="PVI-wd-HVI" secondAttribute="bottom" constant="14" id="gky-f9-L3c"/>
<constraint firstAttribute="trailing" secondItem="7eA-bJ-coY" secondAttribute="trailing" constant="12" id="h1O-eP-2R5"/>
<constraint firstItem="PVI-wd-HVI" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="jfP-VD-ppb"/>
<constraint firstItem="7eA-bJ-coY" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="12" id="un2-TG-Jhd"/>
<constraint firstItem="yJV-Vb-Cna" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="xQS-S1-sFI"/>
</constraints>
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
<size key="customSize" width="160" height="194"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="6"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="layer.borderWidth">
<integer key="value" value="1"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="string" keyPath="backgroundColorName" value="white"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="avatar" destination="PVI-wd-HVI" id="zzN-X5-o2N"/>
<outlet property="detailsButton" destination="yJV-Vb-Cna" id="UE3-4n-7dC"/>
<outlet property="proContainer" destination="C9b-m0-Yo8" id="gUI-wn-gtQ"/>
<outlet property="proLabel" destination="vHm-DF-mQY" id="31b-6G-yvb"/>
<outlet property="subtitleLabel" destination="fEh-1z-Iwj" id="anX-gC-URG"/>
<outlet property="titleLabel" destination="7eA-bJ-coY" id="hpL-Ce-BRw"/>
</connections>
<point key="canvasLocation" x="220.28985507246378" y="184.82142857142856"/>
</collectionViewCell>
</objects>
</document>

View file

@ -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 <vector>
namespace discovery
{
class DiscoveryControllerViewModel
{
public:
void SetSearchResults(search::Results const & res,
std::vector<search::ProductInfo> 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<locals::LocalExpert> 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<search::ProductInfo> m_productInfos;
void SetResults(search::Results const & res,
std::vector<search::ProductInfo> 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<locals::LocalExpert> m_experts;
};
} // namespace discovery

View file

@ -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
}
}

View file

@ -1,131 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="DiscoveryLocalExpertCell" id="bjC-eX-cME" customClass="MWMDiscoveryLocalExpertCell" propertyAccessControl="all">
<rect key="frame" x="0.0" y="0.0" width="160" height="196"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="160" height="196"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="WIp-9D-zCf">
<rect key="frame" x="40" y="12" width="80" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="SzI-04-VFX"/>
<constraint firstAttribute="width" constant="80" id="aTd-ud-dX2"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="40"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WvH-Ak-fYM">
<rect key="frame" x="12" y="104" width="136" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="136" id="0qb-xy-rRb"/>
<constraint firstAttribute="height" constant="20" id="D3X-Qh-8pr"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="fontName" value="medium14"/>
<userDefinedRuntimeAttribute type="string" keyPath="colorName" value="blackPrimaryText"/>
</userDefinedRuntimeAttributes>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KmA-vt-YxO" customClass="RatingSummaryView" customModule="maps_me" customModuleProvider="target">
<rect key="frame" x="56" y="128" width="48" height="20"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" constant="48" id="6WJ-qD-apL"/>
<constraint firstAttribute="height" constant="20" id="jcd-OG-vjG"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="image" keyPath="excellentImage" value="ic_24px_rating_excellent"/>
<userDefinedRuntimeAttribute type="image" keyPath="goodImage" value="ic_24px_rating_excellent"/>
<userDefinedRuntimeAttribute type="image" keyPath="normalImage" value="ic_24px_rating_normal"/>
<userDefinedRuntimeAttribute type="image" keyPath="badImage" value="ic_24px_rating_bad"/>
<userDefinedRuntimeAttribute type="image" keyPath="horribleImage" value="ic_24px_rating_horrible"/>
<userDefinedRuntimeAttribute type="number" keyPath="topOffset">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="bottomOffset">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="leadingImageOffset">
<real key="value" value="6"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="margin">
<real key="value" value="4"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="trailingTextOffset">
<real key="value" value="6"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="image" keyPath="noValueImage" value="ic_12px_rating_normal"/>
</userDefinedRuntimeAttributes>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5vR-oH-2gt">
<rect key="frame" x="0.0" y="156" width="160" height="40"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.040000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="160" id="NFU-3P-bPD"/>
<constraint firstAttribute="height" constant="40" id="Yyw-mG-8Io"/>
</constraints>
<state key="normal" title="Button"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="textColorName" value="linkBlue"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="tapOnPrice" destination="bjC-eX-cME" eventType="touchUpInside" id="Stz-gF-ZeN"/>
</connections>
</button>
</subviews>
</view>
<constraints>
<constraint firstItem="5vR-oH-2gt" firstAttribute="centerX" secondItem="bjC-eX-cME" secondAttribute="centerX" id="A7d-cI-icR"/>
<constraint firstItem="WIp-9D-zCf" firstAttribute="top" secondItem="bjC-eX-cME" secondAttribute="top" constant="12" id="Hdr-qF-Wyu"/>
<constraint firstItem="WvH-Ak-fYM" firstAttribute="centerX" secondItem="WIp-9D-zCf" secondAttribute="centerX" id="JT7-a1-XfK"/>
<constraint firstItem="KmA-vt-YxO" firstAttribute="centerX" secondItem="WvH-Ak-fYM" secondAttribute="centerX" id="ai8-rn-tev"/>
<constraint firstItem="WIp-9D-zCf" firstAttribute="centerX" secondItem="bjC-eX-cME" secondAttribute="centerX" id="fbj-br-6kW"/>
<constraint firstItem="KmA-vt-YxO" firstAttribute="top" secondItem="WvH-Ak-fYM" secondAttribute="bottom" constant="4" id="iw3-ld-rqj"/>
<constraint firstAttribute="bottom" secondItem="5vR-oH-2gt" secondAttribute="bottom" priority="750" id="lUj-bi-oC1"/>
<constraint firstItem="5vR-oH-2gt" firstAttribute="top" secondItem="KmA-vt-YxO" secondAttribute="bottom" constant="8" id="pDT-4d-d9L"/>
<constraint firstItem="WvH-Ak-fYM" firstAttribute="top" secondItem="WIp-9D-zCf" secondAttribute="bottom" constant="12" id="uvh-8Y-Ed6"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="6"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="layer.borderWidth">
<integer key="value" value="1"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="string" keyPath="backgroundColorName" value="white"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="avatar" destination="WIp-9D-zCf" id="NO6-oZ-fBH"/>
<outlet property="name" destination="WvH-Ak-fYM" id="SDk-7F-Ff1"/>
<outlet property="price" destination="5vR-oH-2gt" id="IFd-Z9-x8k"/>
<outlet property="rating" destination="KmA-vt-YxO" id="fp1-El-5cn"/>
</connections>
<point key="canvasLocation" x="-165" y="-28"/>
</collectionViewCell>
</objects>
<resources>
<image name="ic_12px_rating_normal" width="12" height="12"/>
<image name="ic_24px_rating_bad" width="24" height="24"/>
<image name="ic_24px_rating_excellent" width="24" height="24"/>
<image name="ic_24px_rating_horrible" width="24" height="24"/>
<image name="ic_24px_rating_normal" width="24" height="24"/>
</resources>
</document>

View file

@ -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?()
}
}

View file

@ -1,93 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="DiscoveryOnlineTemplateCell" rowHeight="218" id="9IM-3s-P1U" customClass="MWMDiscoveryOnlineTemplateCell" propertyAccessControl="all">
<rect key="frame" x="0.0" y="0.0" width="375" height="218"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="9IM-3s-P1U" id="RFj-It-lNu">
<rect key="frame" x="0.0" y="0.0" width="375" height="217.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UMh-FO-rZJ">
<rect key="frame" x="20" y="79" width="335" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="XSe-2e-PrA"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="fontName" value="medium14"/>
<userDefinedRuntimeAttribute type="string" keyPath="colorName" value="blackPrimaryText"/>
</userDefinedRuntimeAttributes>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Jk-er-UUa">
<rect key="frame" x="20" y="99" width="335" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="e1q-4W-JhV"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="fontName" value="regular12"/>
<userDefinedRuntimeAttribute type="string" keyPath="colorName" value="blackSecondaryText"/>
</userDefinedRuntimeAttributes>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="dCP-Yu-fFz">
<rect key="frame" x="175" y="47" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="JmM-dN-DAf"/>
<constraint firstAttribute="height" constant="24" id="RP9-kK-iAl"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WoO-mw-61q">
<rect key="frame" x="0.0" y="177" width="375" height="40"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.040000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="JKj-h8-ljn"/>
</constraints>
<state key="normal" title="DETAILS"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="details"/>
<userDefinedRuntimeAttribute type="string" keyPath="textColorName" value="linkBlue"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="onTap" destination="9IM-3s-P1U" eventType="touchUpInside" id="wGN-bL-0IS"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="UMh-FO-rZJ" firstAttribute="top" secondItem="dCP-Yu-fFz" secondAttribute="bottom" constant="8" id="BY1-6k-WBn"/>
<constraint firstAttribute="trailing" secondItem="UMh-FO-rZJ" secondAttribute="trailing" constant="20" id="Rsw-ey-7z8"/>
<constraint firstItem="WoO-mw-61q" firstAttribute="top" secondItem="4Jk-er-UUa" secondAttribute="bottom" constant="62" id="afG-Ho-xVJ"/>
<constraint firstAttribute="trailing" secondItem="WoO-mw-61q" secondAttribute="trailing" id="c97-re-08v"/>
<constraint firstItem="4Jk-er-UUa" firstAttribute="leading" secondItem="RFj-It-lNu" secondAttribute="leading" constant="20" id="iEA-Ea-7qS"/>
<constraint firstItem="WoO-mw-61q" firstAttribute="leading" secondItem="RFj-It-lNu" secondAttribute="leading" id="leN-MX-iDP"/>
<constraint firstAttribute="bottom" secondItem="WoO-mw-61q" secondAttribute="bottom" priority="750" id="njG-qV-vDU"/>
<constraint firstItem="dCP-Yu-fFz" firstAttribute="centerX" secondItem="RFj-It-lNu" secondAttribute="centerX" id="p1k-Ug-WjO"/>
<constraint firstItem="UMh-FO-rZJ" firstAttribute="leading" secondItem="RFj-It-lNu" secondAttribute="leading" constant="20" id="pOY-Nu-Av9"/>
<constraint firstItem="4Jk-er-UUa" firstAttribute="top" secondItem="UMh-FO-rZJ" secondAttribute="bottom" constant="4" id="uED-OK-PG4"/>
<constraint firstItem="dCP-Yu-fFz" firstAttribute="top" secondItem="RFj-It-lNu" secondAttribute="top" constant="47" id="wjy-Fv-MDg"/>
<constraint firstAttribute="trailing" secondItem="4Jk-er-UUa" secondAttribute="trailing" constant="20" id="xiC-I1-7yp"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="spinner" destination="dCP-Yu-fFz" id="0nk-0c-1AG"/>
<outlet property="subtitle" destination="4Jk-er-UUa" id="oHh-a4-HLJ"/>
<outlet property="title" destination="UMh-FO-rZJ" id="Bqr-Qj-R0g"/>
</connections>
<point key="canvasLocation" x="-187.5" y="105"/>
</tableViewCell>
</objects>
</document>

View file

@ -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

View file

@ -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

View file

@ -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<locals::LocalExpert> 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<void(search::Results const & res, vector<search::ProductInfo> const & productInfo,
m2::PointD const & viewportCenter, ItemType const type)>;
using SetLocalExperts = function<void(vector<locals::LocalExpert> const & experts)>;
using SetPromoCityGallery = function<void(promo::CityGallery const & experts)>;
using RefreshSection = function<void(ItemType const type)>;
SetSearchResults m_setSearchResults;
SetLocalExperts m_setLocalExperts;
SetPromoCityGallery m_setPromoCityGallery;
RefreshSection m_refreshSection;
};
} // namespace
@ -78,54 +80,67 @@ struct Callback
@interface MWMDiscoveryController ()<MWMDiscoveryTapDelegate>
{
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<search::ProductInfo> 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<ItemType> {
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<ItemType> types = getTypes(self.canUseNetwork);
self.tableManager = [[MWMDiscoveryTableManager alloc] initWithTableView:self.tableView
canUseNetwork:self.canUseNetwork
delegate:self
model:callback];
vector<ItemType> 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<int>(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<int>(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<UIViewController *> * 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<UIViewController *> * controllers = [self.navigationController.viewControllers mutableCopy];
[controllers addObjectsFromArray:@[bookmarks, catalog]];
[self.navigationController setViewControllers:controllers animated:YES];
break;
}
}
@end

View file

@ -0,0 +1,23 @@
#include "map/search_product_info.hpp"
#include "search/result.hpp"
#include "geometry/point2d.hpp"
#include <vector>
NS_ASSUME_NONNULL_BEGIN
@interface MWMDiscoveryMapObjects : NSObject
- (instancetype)initWithSearchResults:(search::Results const &)results
productInfos:(std::vector<search::ProductInfo> 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

View file

@ -0,0 +1,43 @@
#import "MWMDiscoveryMapObjects.h"
@interface MWMDiscoveryMapObjects() {
search::Results m_results;
std::vector<search::ProductInfo> m_productInfos;
m2::PointD m_viewportCenter;
}
@end
@implementation MWMDiscoveryMapObjects
- (instancetype)initWithSearchResults:(search::Results const &)results
productInfos:(std::vector<search::ProductInfo> 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

View file

@ -5,20 +5,21 @@
namespace discovery
{
class DiscoveryControllerViewModel;
NSString * StatProvider(ItemType const type);
} // namespace discovery
using GetModelCallback = std::function<discovery::DiscoveryControllerViewModel const &()>;
@class MWMDiscoveryControllerViewModel;
typedef MWMDiscoveryControllerViewModel *(^MWMGetModelCallback)(void);
@protocol MWMDiscoveryTapDelegate;
@interface MWMDiscoveryTableManager : NSObject
- (instancetype)initWithTableView:(UITableView *)tableView
canUseNetwork:(BOOL)canUseNetwork
delegate:(id<MWMDiscoveryTapDelegate>)delegate
model:(GetModelCallback &&)modelCallback;
model:(MWMGetModelCallback)modelCallback;
- (void)loadItems:(std::vector<discovery::ItemType> const &)types;
- (void)reloadItem:(discovery::ItemType const)type;

View file

@ -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<NSString *, MWMRatingSummaryViewValueType> FormattedRating(float const rawValue)
{
auto const str = place_page::rating::GetRatingFormatted(rawValue);
auto const impress = static_cast<MWMRatingSummaryViewValueType>(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<MWMDiscoveryTapDelegate> 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<MWMDiscoveryTapDelegate>)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<ItemType> const &)types
{
- (MWMDiscoveryControllerViewModel *)viewModel {
return self.model();
}
- (void)loadItems:(vector<ItemType> 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<int>(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<int>(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<MWMDiscoverySearchCollectionHolderCell *>(
[self.tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]);
auto collection = static_cast<MWMDiscoveryCollectionView *>(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<MWMDiscoveryLocalExpertCollectionHolderCell *>(
[self.tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]);
auto collection = static_cast<MWMDiscoveryCollectionView *>(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<MWMDiscoveryBookingCollectionHolderCell *>(
[self.tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]);
auto collection = static_cast<MWMDiscoveryCollectionView *>(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<NSInteger>(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<MWMDiscoveryNoResultsCell *>(
[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<MWMDiscoveryOnlineTemplateCell *>(
[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<MWMDiscoverySpinnerCell *>(
[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<NSInteger>(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<MWMDiscoveryMoreCell *>([collectionView
dequeueReusableCellWithCellClass:cls
indexPath:indexPath]);
return cell;
}
Class cls = [MWMDiscoverySearchCell class];
auto cell = static_cast<MWMDiscoverySearchCell *>(
[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<MWMDiscoveryMoreCell *>([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<MWMDiscoveryLocalExpertCell *>(
[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<MWMDiscoveryMoreCell *>([collectionView
dequeueReusableCellWithCellClass:cls
indexPath:indexPath]);
return cell;
}
Class cls = [MWMDiscoveryBookingCell class];
auto cell = static_cast<MWMDiscoveryBookingCell *>(
[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<MWMDiscoveryMoreCell *>([collectionView
dequeueReusableCellWithCellClass:cls
indexPath:indexPath]);
return cell;
}
case ItemType::LocalExperts:
return [[UICollectionViewCell alloc] init];
}
}

View file

@ -2,8 +2,9 @@
@protocol MWMDiscoveryTapDelegate<NSObject>
- (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

View file

@ -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?()
}
}

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="DiscoveryOnlineTemplateCell" rowHeight="266" id="9IM-3s-P1U" customClass="MWMDiscoveryOnlineTemplateCell">
<rect key="frame" x="0.0" y="0.0" width="375" height="266"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="9IM-3s-P1U" id="RFj-It-lNu">
<rect key="frame" x="0.0" y="0.0" width="375" height="265.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QHe-qU-OhV">
<rect key="frame" x="8" y="44" width="359" height="221.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UMh-FO-rZJ">
<rect key="frame" x="20" y="79" width="319" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="XSe-2e-PrA"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="fontName" value="medium14"/>
<userDefinedRuntimeAttribute type="string" keyPath="colorName" value="blackPrimaryText"/>
</userDefinedRuntimeAttributes>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Jk-er-UUa">
<rect key="frame" x="20" y="99" width="319" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="fontName" value="regular12"/>
<userDefinedRuntimeAttribute type="string" keyPath="colorName" value="blackSecondaryText"/>
</userDefinedRuntimeAttributes>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="dCP-Yu-fFz">
<rect key="frame" x="167.5" y="47" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="JmM-dN-DAf"/>
<constraint firstAttribute="height" constant="24" id="RP9-kK-iAl"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WoO-mw-61q">
<rect key="frame" x="0.0" y="181.5" width="359" height="40"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.040000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="JKj-h8-ljn"/>
</constraints>
<state key="normal" title="DETAILS"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="details"/>
<userDefinedRuntimeAttribute type="string" keyPath="textColorName" value="linkBlue"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="onTap" destination="9IM-3s-P1U" eventType="touchUpInside" id="wGN-bL-0IS"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="UMh-FO-rZJ" secondAttribute="trailing" constant="20" id="EhK-cf-zIl"/>
<constraint firstItem="WoO-mw-61q" firstAttribute="top" secondItem="4Jk-er-UUa" secondAttribute="bottom" constant="62" id="KuK-fw-2Rl"/>
<constraint firstItem="4Jk-er-UUa" firstAttribute="top" secondItem="UMh-FO-rZJ" secondAttribute="bottom" constant="4" id="OO0-G5-dtz"/>
<constraint firstAttribute="trailing" secondItem="WoO-mw-61q" secondAttribute="trailing" id="TPX-ju-dpb"/>
<constraint firstItem="UMh-FO-rZJ" firstAttribute="top" secondItem="dCP-Yu-fFz" secondAttribute="bottom" constant="8" id="V6Z-td-GYY"/>
<constraint firstAttribute="bottom" secondItem="WoO-mw-61q" secondAttribute="bottom" id="VtK-N5-abK"/>
<constraint firstItem="UMh-FO-rZJ" firstAttribute="leading" secondItem="QHe-qU-OhV" secondAttribute="leading" constant="20" id="Xj3-co-r3p"/>
<constraint firstAttribute="trailing" secondItem="4Jk-er-UUa" secondAttribute="trailing" constant="20" id="ZpC-EO-4eh"/>
<constraint firstItem="4Jk-er-UUa" firstAttribute="leading" secondItem="QHe-qU-OhV" secondAttribute="leading" constant="20" id="evP-bA-hNa"/>
<constraint firstItem="dCP-Yu-fFz" firstAttribute="top" secondItem="QHe-qU-OhV" secondAttribute="top" constant="47" id="uSN-xL-tc6"/>
<constraint firstItem="WoO-mw-61q" firstAttribute="leading" secondItem="QHe-qU-OhV" secondAttribute="leading" id="xdU-9j-1oK"/>
<constraint firstItem="dCP-Yu-fFz" firstAttribute="centerX" secondItem="QHe-qU-OhV" secondAttribute="centerX" id="y66-M2-Vf5"/>
</constraints>
<viewLayoutGuide key="safeArea" id="jpF-nO-kZW"/>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="t7L-Pc-YZX">
<rect key="frame" x="16" y="12" width="347" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="colorName" value="blackSecondaryText"/>
<userDefinedRuntimeAttribute type="string" keyPath="fontName" value="medium14"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="t7L-Pc-YZX" firstAttribute="leading" secondItem="RFj-It-lNu" secondAttribute="leading" constant="16" id="7Rf-Gu-Wi8"/>
<constraint firstAttribute="trailing" secondItem="t7L-Pc-YZX" secondAttribute="trailing" constant="12" id="8Tr-Aa-zs5"/>
<constraint firstAttribute="trailing" secondItem="QHe-qU-OhV" secondAttribute="trailing" constant="8" id="QPQ-SE-hkT"/>
<constraint firstItem="QHe-qU-OhV" firstAttribute="leading" secondItem="RFj-It-lNu" secondAttribute="leading" constant="8" id="TrD-vm-3uZ"/>
<constraint firstAttribute="bottom" secondItem="QHe-qU-OhV" secondAttribute="bottom" id="oan-1Y-uuz"/>
<constraint firstItem="QHe-qU-OhV" firstAttribute="top" secondItem="t7L-Pc-YZX" secondAttribute="bottom" constant="12" id="pKH-cU-c4t"/>
<constraint firstItem="t7L-Pc-YZX" firstAttribute="top" secondItem="RFj-It-lNu" secondAttribute="top" constant="12" id="xTp-3J-4fU"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<outlet property="actionButton" destination="WoO-mw-61q" id="uVo-Xb-CnX"/>
<outlet property="containerView" destination="QHe-qU-OhV" id="jlb-3b-Nms"/>
<outlet property="header" destination="t7L-Pc-YZX" id="zw1-e1-mc1"/>
<outlet property="spinner" destination="dCP-Yu-fFz" id="0nk-0c-1AG"/>
<outlet property="subtitle" destination="4Jk-er-UUa" id="oHh-a4-HLJ"/>
<outlet property="title" destination="UMh-FO-rZJ" id="Bqr-Qj-R0g"/>
</connections>
<point key="canvasLocation" x="-188" y="125.93703148425789"/>
</tableViewCell>
</objects>
</document>

View file

@ -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)
}
}

View file

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="DiscoveryLocalExpertCollectionHolderCell" rowHeight="245" id="kOb-uR-NfL" customClass="MWMDiscoveryLocalExpertCollectionHolderCell" propertyAccessControl="all">
<rect key="frame" x="0.0" y="0.0" width="375" height="248"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="kOb-uR-NfL" id="HJe-Ad-Mjp">
<rect key="frame" x="0.0" y="0.0" width="375" height="247.5"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="DiscoveryGuideCollectionHolderCell" rowHeight="235" id="KGk-i7-Jjw" customClass="MWMDiscoveryGuideCollectionHolderCell">
<rect key="frame" x="0.0" y="0.0" width="391" height="235"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="391" height="234.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mVI-Rn-1Ht">
<rect key="frame" x="16" y="12" width="327" height="26"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BeQ-ih-VTq">
<rect key="frame" x="16" y="12" width="363" height="15.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@ -29,14 +29,14 @@
<userDefinedRuntimeAttribute type="string" keyPath="fontName" value="medium14"/>
</userDefinedRuntimeAttributes>
</label>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="hb0-w1-X0p" customClass="MWMDiscoveryCollectionView">
<rect key="frame" x="0.0" y="50" width="375" height="197"/>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="NFk-MT-sfu" customClass="MWMDiscoveryCollectionView">
<rect key="frame" x="0.0" y="39.5" width="391" height="195"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="197" id="EJY-mn-vpT"/>
<constraint firstAttribute="height" constant="195" id="Nkd-D4-A27"/>
</constraints>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="8" minimumInteritemSpacing="0.0" id="XgH-zm-NQn">
<size key="itemSize" width="160" height="196"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="8" minimumInteritemSpacing="0.0" id="QhS-4e-r8U">
<size key="itemSize" width="160" height="138"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="8" minY="0.0" maxX="8" maxY="0.0"/>
@ -45,27 +45,22 @@
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="hb0-w1-X0p" firstAttribute="top" secondItem="mVI-Rn-1Ht" secondAttribute="bottom" constant="12" id="2HX-P3-y34"/>
<constraint firstAttribute="trailingMargin" secondItem="mVI-Rn-1Ht" secondAttribute="trailing" constant="16" id="Krm-7b-e90"/>
<constraint firstItem="mVI-Rn-1Ht" firstAttribute="leading" secondItem="HJe-Ad-Mjp" secondAttribute="leading" constant="16" id="O0N-tB-zHS"/>
<constraint firstItem="mVI-Rn-1Ht" firstAttribute="top" secondItem="HJe-Ad-Mjp" secondAttribute="top" constant="12" id="Pjh-uR-2fV"/>
<constraint firstAttribute="trailing" secondItem="hb0-w1-X0p" secondAttribute="trailing" id="TZ9-cR-8ob"/>
<constraint firstAttribute="bottom" secondItem="hb0-w1-X0p" secondAttribute="bottom" id="ekR-z5-imZ"/>
<constraint firstItem="hb0-w1-X0p" firstAttribute="leading" secondItem="HJe-Ad-Mjp" secondAttribute="leading" id="uPC-mg-zsI"/>
<constraint firstItem="NFk-MT-sfu" firstAttribute="top" secondItem="BeQ-ih-VTq" secondAttribute="bottom" constant="12" id="1gL-Ci-yMY"/>
<constraint firstItem="NFk-MT-sfu" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="8ct-AF-P13"/>
<constraint firstItem="BeQ-ih-VTq" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="12" id="C1z-tx-ppW"/>
<constraint firstItem="BeQ-ih-VTq" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="Inc-RA-BDU"/>
<constraint firstAttribute="bottom" secondItem="NFk-MT-sfu" secondAttribute="bottom" id="IsQ-zk-Inu"/>
<constraint firstAttribute="trailing" secondItem="NFk-MT-sfu" secondAttribute="trailing" id="ZFO-zN-y6P"/>
<constraint firstAttribute="trailing" secondItem="BeQ-ih-VTq" secondAttribute="trailing" constant="12" id="jkL-TY-BMW"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="backgroundColorName" value="pressBackground"/>
</userDefinedRuntimeAttributes>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="backgroundColorName" value="pressBackground"/>
</userDefinedRuntimeAttributes>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="collectionView" destination="hb0-w1-X0p" id="CRZ-o9-rpF"/>
<outlet property="header" destination="mVI-Rn-1Ht" id="0SB-Lz-CRU"/>
<outlet property="collectionView" destination="NFk-MT-sfu" id="VEm-Jg-B49"/>
<outlet property="header" destination="BeQ-ih-VTq" id="lvK-TC-7VT"/>
</connections>
<point key="canvasLocation" x="-179.5" y="58"/>
<point key="canvasLocation" x="190.57971014492756" y="225"/>
</tableViewCell>
</objects>
</document>

View file

@ -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

View file

@ -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 &center = [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 &center = [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 &center = [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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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