diff --git a/iphone/CoreApi/CoreApi.xcodeproj/project.pbxproj b/iphone/CoreApi/CoreApi.xcodeproj/project.pbxproj index 459a51a4c3..1c0c86d49e 100644 --- a/iphone/CoreApi/CoreApi.xcodeproj/project.pbxproj +++ b/iphone/CoreApi/CoreApi.xcodeproj/project.pbxproj @@ -87,6 +87,11 @@ AC6A585728057EF6003EABAF /* StringUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = AC6A585628057CC1003EABAF /* StringUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; ED0B1FF42CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.h in Headers */ = {isa = PBXBuildFile; fileRef = ED0B1FF22CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.h */; settings = {ATTRIBUTES = (Public, ); }; }; ED0B1FF52CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED0B1FF32CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.mm */; }; + ED49D7222CED0395004AF27E /* ProductsConfiguration+Core.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED49D7202CED0395004AF27E /* ProductsConfiguration+Core.mm */; }; + ED49D7242CED03AC004AF27E /* ProductsConfiguration+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = ED49D7232CED03A8004AF27E /* ProductsConfiguration+Core.h */; }; + ED49D75B2CEF8BD2004AF27E /* ProductsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED49D7562CEF850F004AF27E /* ProductsConfiguration.swift */; }; + ED49D75F2CEFA8C0004AF27E /* Product+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = ED49D75E2CEFA8C0004AF27E /* Product+Core.h */; }; + ED49D7612CEFA8E1004AF27E /* Product+Core.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED49D7602CEFA8E1004AF27E /* Product+Core.mm */; }; ED965B0D2CD67A470049E39E /* DistanceFormatter.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED965B0A2CD67A470049E39E /* DistanceFormatter.mm */; }; ED965B102CD67A470049E39E /* DistanceFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = ED965B092CD67A470049E39E /* DistanceFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; }; ED965B132CD67A9B0049E39E /* AltitudeFormatter.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED965B122CD67A9B0049E39E /* AltitudeFormatter.mm */; }; @@ -187,6 +192,11 @@ AC6A585628057CC1003EABAF /* StringUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringUtils.h; sourceTree = ""; }; ED0B1FF22CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeepLinkInAppFeatureHighlightData.h; sourceTree = ""; }; ED0B1FF32CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DeepLinkInAppFeatureHighlightData.mm; sourceTree = ""; }; + ED49D7202CED0395004AF27E /* ProductsConfiguration+Core.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "ProductsConfiguration+Core.mm"; sourceTree = ""; }; + ED49D7232CED03A8004AF27E /* ProductsConfiguration+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ProductsConfiguration+Core.h"; sourceTree = ""; }; + ED49D7562CEF850F004AF27E /* ProductsConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsConfiguration.swift; sourceTree = ""; }; + ED49D75E2CEFA8C0004AF27E /* Product+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Product+Core.h"; sourceTree = ""; }; + ED49D7602CEFA8E1004AF27E /* Product+Core.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "Product+Core.mm"; sourceTree = ""; }; ED965B092CD67A470049E39E /* DistanceFormatter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DistanceFormatter.h; sourceTree = ""; }; ED965B0A2CD67A470049E39E /* DistanceFormatter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DistanceFormatter.mm; sourceTree = ""; }; ED965B112CD67A9B0049E39E /* AltitudeFormatter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AltitudeFormatter.h; sourceTree = ""; }; @@ -310,6 +320,11 @@ 47F701ED238C86F000D18E95 /* PlacePageButtonsData.h */, 47F701F1238C877C00D18E95 /* PlacePageButtonsData+Core.h */, 47F701EE238C86F000D18E95 /* PlacePageButtonsData.mm */, + ED49D7562CEF850F004AF27E /* ProductsConfiguration.swift */, + ED49D7232CED03A8004AF27E /* ProductsConfiguration+Core.h */, + ED49D7202CED0395004AF27E /* ProductsConfiguration+Core.mm */, + ED49D75E2CEFA8C0004AF27E /* Product+Core.h */, + ED49D7602CEFA8E1004AF27E /* Product+Core.mm */, ); path = Common; sourceTree = ""; @@ -487,6 +502,7 @@ 9957FADB237ACB1100855F48 /* DeepLinkSearchData.h in Headers */, 479F7056234FB7F200011E2E /* MWMBookmarksManager.h in Headers */, 47942D83237CC52E00DEFAE3 /* MWMOpeningHours.h in Headers */, + ED49D7242CED03AC004AF27E /* ProductsConfiguration+Core.h in Headers */, 4718C4322355FC3C00640DF1 /* MWMNetworkPolicy.h in Headers */, 47CA68EC2506F6F100671019 /* MWMTrack+Core.h in Headers */, 47F4F1FD23A3D1AC0022FD56 /* MWMMapNodeAttributes+Core.h in Headers */, @@ -496,6 +512,7 @@ 47942DA0237D954400DEFAE3 /* PlacePageBookmarkData+Core.h in Headers */, 479F704B234F78AB00011E2E /* MWMFrameworkHelper.h in Headers */, 47D9019923AC236100D9364C /* MWMMapUpdateInfo+Core.h in Headers */, + ED49D75F2CEFA8C0004AF27E /* Product+Core.h in Headers */, 47942D6E237CC3E800DEFAE3 /* PlacePagePreviewData+Core.h in Headers */, 47942D87237CC55800DEFAE3 /* MWMOpeningHoursCommon.h in Headers */, 47942DAB237ED9FE00DEFAE3 /* IOpeningHoursLocalization.h in Headers */, @@ -615,18 +632,21 @@ 47D9019623AC22E500D9364C /* MWMMapUpdateInfo.mm in Sources */, 47CA68E42506D29000671019 /* MWMBookmarkColor.mm in Sources */, 471AB98E23AB925D00F56D49 /* MWMMapSearchResult.mm in Sources */, + ED49D7612CEFA8E1004AF27E /* Product+Core.mm in Sources */, 47C637D62354AEBE00E12DE0 /* MWMMapOverlayManager.mm in Sources */, 475784C32344B422008291A4 /* Framework.cpp in Sources */, 4718C4332355FC3C00640DF1 /* MWMNetworkPolicy.mm in Sources */, 47CA68E92506F61400671019 /* MWMTrack.mm in Sources */, 472602A924092C5B00731135 /* MWMGeoUtil.mm in Sources */, 47F701F0238C86F000D18E95 /* PlacePageButtonsData.mm in Sources */, + ED49D7222CED0395004AF27E /* ProductsConfiguration+Core.mm in Sources */, 9940622123EAC57900493D1A /* ElevationHeightPoint.m in Sources */, ED965B2A2CDA1C440049E39E /* DateTimeFormatter.swift in Sources */, 47EEAFF42350CEDB005CF316 /* AppInfo.mm in Sources */, 47E8163623B1889C008FD836 /* MWMStorage.mm in Sources */, ED965B0D2CD67A470049E39E /* DistanceFormatter.mm in Sources */, EDC4E3512C5D222D009286A2 /* RecentlyDeletedCategory.mm in Sources */, + ED49D75B2CEF8BD2004AF27E /* ProductsConfiguration.swift in Sources */, 47CA68DE2502022400671019 /* MWMBookmark.mm in Sources */, 9957FAE9237AE5B000855F48 /* Logger.mm in Sources */, ED965B222CD8F5AA0049E39E /* DurationFormatter.mm in Sources */, @@ -643,7 +663,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = NO; + DEFINES_MODULE = YES; GCC_PRECOMPILE_PREFIX_HEADER = NO; HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -665,7 +685,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = NO; + DEFINES_MODULE = YES; ENABLE_NS_ASSERTIONS = NO; GCC_PRECOMPILE_PREFIX_HEADER = NO; HEADER_SEARCH_PATHS = ( diff --git a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.h b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.h index 8bae421b92..c9c06a2fe7 100644 --- a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.h +++ b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.h @@ -7,6 +7,13 @@ typedef NS_ENUM(NSUInteger, MWMZoomMode) { MWMZoomModeIn = 0, MWMZoomModeOut }; +typedef NS_ENUM(NSInteger, ProductsPopupCloseReason) { + ProductsPopupCloseReasonClose, + ProductsPopupCloseReasonSelectProduct, + ProductsPopupCloseReasonAlreadyDonated, + ProductsPopupCloseReasonRemindLater +}; + NS_ASSUME_NONNULL_BEGIN typedef void (^SearchInDownloaderCompletions)(NSArray *results, BOOL finished); @@ -21,8 +28,19 @@ typedef void (^SearchInDownloaderCompletions)(NSArray *res @end +@class ProductsConfiguration; +@class Product; + +@protocol ProductsManager + ++ (nullable ProductsConfiguration *)getProductsConfiguration; ++ (void)didCloseProductsPopupWithReason:(ProductsPopupCloseReason)reason; ++ (void)didSelectProduct:(Product *)product; + +@end + NS_SWIFT_NAME(FrameworkHelper) -@interface MWMFrameworkHelper : NSObject +@interface MWMFrameworkHelper : NSObject + (void)processFirstLaunch:(BOOL)hasLocation; + (void)setVisibleViewport:(CGRect)rect scaleFactor:(CGFloat)scale; diff --git a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm index 57febe24ea..30c826bedc 100644 --- a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm +++ b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm @@ -1,5 +1,7 @@ #import "MWMFrameworkHelper.h" #import "MWMMapSearchResult+Core.h" +#import "ProductsConfiguration+Core.h" +#import "Product+Core.h" #include "Framework.h" @@ -8,6 +10,19 @@ #include "platform/local_country_file_utils.hpp" #include "platform/network_policy_ios.h" +static Framework::ProductsPopupCloseReason ConvertProductPopupCloseReasonToCore(ProductsPopupCloseReason reason) { + switch (reason) { + case ProductsPopupCloseReasonClose: + return Framework::ProductsPopupCloseReason::Close; + case ProductsPopupCloseReasonSelectProduct: + return Framework::ProductsPopupCloseReason::SelectProduct; + case ProductsPopupCloseReasonAlreadyDonated: + return Framework::ProductsPopupCloseReason::AlreadyDonated; + case ProductsPopupCloseReasonRemindLater: + return Framework::ProductsPopupCloseReason::RemindLater; + } +} + @implementation MWMFrameworkHelper + (void)processFirstLaunch:(BOOL)hasLocation { @@ -196,6 +211,8 @@ return GetFramework().GetDrawScale(); } +// MARK: - TrackRecorder + + (void)startTrackRecording { GetFramework().StartTrackRecording(); } @@ -216,4 +233,19 @@ return GetFramework().IsTrackRecordingEmpty(); } +// MARK: - ProductsManager + ++ (nullable ProductsConfiguration *)getProductsConfiguration { + auto const & config = GetFramework().GetProductsConfiguration(); + return config.has_value() ? [[ProductsConfiguration alloc] init:config.value()] : nil; +} + ++ (void)didCloseProductsPopupWithReason:(ProductsPopupCloseReason)reason { + GetFramework().DidCloseProductsPopup(ConvertProductPopupCloseReasonToCore(reason)); +} + ++ (void)didSelectProduct:(Product *)product { + GetFramework().DidSelectProduct({product.title.UTF8String, product.link.UTF8String}); +} + @end diff --git a/iphone/CoreApi/CoreApi/PlacePageData/Common/Product+Core.h b/iphone/CoreApi/CoreApi/PlacePageData/Common/Product+Core.h new file mode 100644 index 0000000000..1348138ad1 --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/Common/Product+Core.h @@ -0,0 +1,10 @@ +#include +#include + +#include "platform/products.hpp" + +@interface Product (Core) + +- (nonnull instancetype)init:(products::ProductsConfig::Product const &)product; + +@end diff --git a/iphone/CoreApi/CoreApi/PlacePageData/Common/Product+Core.mm b/iphone/CoreApi/CoreApi/PlacePageData/Common/Product+Core.mm new file mode 100644 index 0000000000..fbcd0b11b4 --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/Common/Product+Core.mm @@ -0,0 +1,11 @@ +#import "Product+Core.h" + +@implementation Product (Core) + +- (nonnull instancetype)init:(products::ProductsConfig::Product const &)product { + self = [self initWithTitle:[NSString stringWithCString:product.GetTitle().c_str() encoding:NSUTF8StringEncoding] + link:[NSString stringWithCString:product.GetLink().c_str() encoding:NSUTF8StringEncoding]]; + return self; +} + +@end diff --git a/iphone/CoreApi/CoreApi/PlacePageData/Common/ProductsConfiguration+Core.h b/iphone/CoreApi/CoreApi/PlacePageData/Common/ProductsConfiguration+Core.h new file mode 100644 index 0000000000..0225e3adf7 --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/Common/ProductsConfiguration+Core.h @@ -0,0 +1,14 @@ +#include +#include + +#include "platform/products.hpp" + +NS_ASSUME_NONNULL_BEGIN + +@interface ProductsConfiguration (Core) + +- (nonnull instancetype)init:(products::ProductsConfig const &)config; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/CoreApi/CoreApi/PlacePageData/Common/ProductsConfiguration+Core.mm b/iphone/CoreApi/CoreApi/PlacePageData/Common/ProductsConfiguration+Core.mm new file mode 100644 index 0000000000..7957ab29b8 --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/Common/ProductsConfiguration+Core.mm @@ -0,0 +1,15 @@ +#import "ProductsConfiguration+Core.h" +#import "Product+Core.h" + +@implementation ProductsConfiguration (Core) + +- (nonnull instancetype)init:(products::ProductsConfig const &)config { + auto const & coreProducts = config.GetProducts(); + NSMutableArray * products = [[NSMutableArray alloc] initWithCapacity:coreProducts.size()]; + for (auto const & product : coreProducts) + [products addObject:[[Product alloc] init:product]]; + self = [self initWithPlacePagePrompt:[NSString stringWithCString:config.GetPlacePagePrompt().c_str() encoding:NSUTF8StringEncoding] products:products]; + return self; +} + +@end diff --git a/iphone/CoreApi/CoreApi/PlacePageData/Common/ProductsConfiguration.swift b/iphone/CoreApi/CoreApi/PlacePageData/Common/ProductsConfiguration.swift new file mode 100644 index 0000000000..db36f5b4d3 --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/Common/ProductsConfiguration.swift @@ -0,0 +1,23 @@ +import Foundation + +@objcMembers +public final class Product: NSObject { + public let title: String + public let link: String + + public init(title: String, link: String) { + self.title = title + self.link = link + } +} + +@objcMembers +public final class ProductsConfiguration: NSObject { + public let placePagePrompt: String + public let products: [Product] + + public init(placePagePrompt: String, products: [Product]) { + self.placePagePrompt = placePagePrompt + self.products = products + } +} diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index 5b0fe55505..1beb1aa047 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -477,6 +477,9 @@ ED1ADA332BC6B1B40029209F /* CarPlayServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1ADA322BC6B1B40029209F /* CarPlayServiceTests.swift */; }; ED3EAC202B03C88100220A4A /* BottomTabBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED3EAC1F2B03C88100220A4A /* BottomTabBarButton.swift */; }; ED43B8BD2C12063500D07BAA /* DocumentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED43B8BC2C12063500D07BAA /* DocumentPicker.swift */; }; + ED4DC7792CAEDECC0029B338 /* ProductsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4DC7742CAEDECC0029B338 /* ProductsViewController.swift */; }; + ED4DC7782CAEDECC0029B338 /* ProductButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4DC7732CAEDECC0029B338 /* ProductButton.swift */; }; + ED4DC7772CAEDECC0029B338 /* ProductsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4DC7752CAEDECC0029B338 /* ProductsViewModel.swift */; }; ED63CEB92BDF8F9D006155C4 /* SettingsTableViewiCloudSwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED63CEB62BDF8F9C006155C4 /* SettingsTableViewiCloudSwitchCell.swift */; }; ED77556E2C2C490B0051E656 /* UIAlertController+openInAppActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED77556D2C2C490B0051E656 /* UIAlertController+openInAppActionSheet.swift */; }; ED79A5AB2BD7AA9C00952D1F /* LoadingOverlayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED79A5AA2BD7AA9C00952D1F /* LoadingOverlayViewController.swift */; }; @@ -1405,6 +1408,9 @@ ED43B8BC2C12063500D07BAA /* DocumentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentPicker.swift; sourceTree = ""; }; ED48BBB817C2B1E2003E7E92 /* CircleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircleView.h; sourceTree = ""; }; ED48BBB917C2B1E2003E7E92 /* CircleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CircleView.m; sourceTree = ""; }; + ED4DC7732CAEDECC0029B338 /* ProductButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductButton.swift; sourceTree = ""; }; + ED4DC7742CAEDECC0029B338 /* ProductsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsViewController.swift; sourceTree = ""; }; + ED4DC7752CAEDECC0029B338 /* ProductsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsViewModel.swift; sourceTree = ""; }; ED63CEB62BDF8F9C006155C4 /* SettingsTableViewiCloudSwitchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewiCloudSwitchCell.swift; sourceTree = ""; }; ED77556D2C2C490B0051E656 /* UIAlertController+openInAppActionSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+openInAppActionSheet.swift"; sourceTree = ""; }; ED79A5AA2BD7AA9C00952D1F /* LoadingOverlayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingOverlayViewController.swift; sourceTree = ""; }; @@ -2943,6 +2949,7 @@ 993DF0C223F6BD0600AC231A /* ElevationDetails */, 99DEF9D523E420D2006BFD21 /* ElevationProfile */, EDE8EAE32C2DB74A002777F5 /* OpenInAppActionSheet */, + ED4DC7762CAEDECC0029B338 /* Products */, ); path = Components; sourceTree = ""; @@ -3114,6 +3121,16 @@ path = DocumentPicker; sourceTree = ""; }; + ED4DC7762CAEDECC0029B338 /* Products */ = { + isa = PBXGroup; + children = ( + ED4DC7732CAEDECC0029B338 /* ProductButton.swift */, + ED4DC7742CAEDECC0029B338 /* ProductsViewController.swift */, + ED4DC7752CAEDECC0029B338 /* ProductsViewModel.swift */, + ); + path = Products; + sourceTree = ""; + }; ED79A5A92BD7AA7500952D1F /* LoadingOverlay */ = { isa = PBXGroup; children = ( @@ -4468,6 +4485,9 @@ 34BF0CC71C31304A00D097EB /* MWMAuthorizationCommon.mm in Sources */, 34AB664D1FC5AA330078E451 /* RouteManagerFooterView.swift in Sources */, 6741A9E01BF340DE002C974C /* MWMDownloaderDialogHeader.mm in Sources */, + ED4DC7772CAEDECC0029B338 /* ProductsViewModel.swift in Sources */, + ED4DC7782CAEDECC0029B338 /* ProductButton.swift in Sources */, + ED4DC7792CAEDECC0029B338 /* ProductsViewController.swift in Sources */, CDCA2748223FD24600167D87 /* MWMCarPlaySearchResultObject.mm in Sources */, 475ED78824C7D0F30063ADC7 /* ValueStepperView.swift in Sources */, 3454D7E01E07F045004AF2AD /* UITextField+RuntimeAttributes.m in Sources */, diff --git a/iphone/Maps/UI/PlacePage/Components/Products/ProductButton.swift b/iphone/Maps/UI/PlacePage/Components/Products/ProductButton.swift new file mode 100644 index 0000000000..7c65df7371 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/Components/Products/ProductButton.swift @@ -0,0 +1,38 @@ +final class ProductButton: UIButton { + + private var action: () -> Void + + init(title: String, action: @escaping () -> Void) { + self.action = action + super.init(frame: .zero) + self.setup(title: title, action: action) + self.layout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setup(title: String, action: @escaping () -> Void) { + setStyleAndApply("BlueBackground") + setTitle(title, for: .normal) + setTitleColor(.white, for: .normal) + titleLabel?.font = UIFont.regular14() + titleLabel?.allowsDefaultTighteningForTruncation = true + titleLabel?.adjustsFontSizeToFitWidth = true + titleLabel?.minimumScaleFactor = 0.5 + layer.setCorner(radius: 5.0) + layer.masksToBounds = true + addTarget(self, action: #selector(buttonDidTap), for: .touchUpInside) + } + + private func layout() { + translatesAutoresizingMaskIntoConstraints = false + heightAnchor.constraint(equalToConstant: 30.0).isActive = true + } + + @objc private func buttonDidTap() { + action() + } +} diff --git a/iphone/Maps/UI/PlacePage/Components/Products/ProductsViewController.swift b/iphone/Maps/UI/PlacePage/Components/Products/ProductsViewController.swift new file mode 100644 index 0000000000..6b7ee5f606 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/Components/Products/ProductsViewController.swift @@ -0,0 +1,162 @@ +final class ProductsViewController: UIViewController { + + private enum Constants { + static let spacing: CGFloat = 10 + static let titleLeadingPadding: CGFloat = 12 + static let titleTrailingPadding: CGFloat = 10 + static let descriptionTopPadding: CGFloat = 10 + static let closeButtonSize: CGFloat = 24 + static let closeButtonTrailingPadding: CGFloat = -12 + static let closeButtonTopPadding: CGFloat = 12 + static let stackViewTopPadding: CGFloat = 12 + static let subtitleButtonTopPadding: CGFloat = 4 + static let subtitleButtonBottomPadding: CGFloat = -4 + } + + private let viewModel: ProductsViewModel + private let titleLabel = UILabel() + private let descriptionLabel = UILabel() + private let closeButton = UIButton(type: .system) + private let stackView = UIStackView() + private let leadingSubtitleButton = UIButton(type: .system) + private let trailingSubtitleButton = UIButton(type: .system) + + init(viewModel: ProductsViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + layout() + } + + private func setupViews() { + view.setStyleAndApply("Background") + setupTitleLabel() + setupDescriptionLabel() + setupCloseButton() + setupProductsStackView() + setupSubtitleButtons() + } + + private func setupTitleLabel() { + titleLabel.text = viewModel.title + titleLabel.font = UIFont.semibold16() + titleLabel.numberOfLines = 1 + titleLabel.translatesAutoresizingMaskIntoConstraints = false + } + + private func setupDescriptionLabel() { + descriptionLabel.text = viewModel.description + descriptionLabel.font = UIFont.regular14() + descriptionLabel.numberOfLines = 0 + descriptionLabel.translatesAutoresizingMaskIntoConstraints = false + } + + private func setupCloseButton() { + closeButton.setStyleAndApply("MWMGray") + closeButton.setImage(UIImage(resource: .icSearchClear), for: .normal) + closeButton.translatesAutoresizingMaskIntoConstraints = false + closeButton.addTarget(self, action: #selector(closeButtonDidTap), for: .touchUpInside) + } + + private func setupProductsStackView() { + stackView.axis = .horizontal + stackView.alignment = .fill + stackView.distribution = .fillEqually + stackView.spacing = Constants.spacing + stackView.translatesAutoresizingMaskIntoConstraints = false + viewModel.products.forEach { product in + let button = ProductButton(title: product.title) { [weak self] in + self?.productButtonDidTap(product) + } + stackView.addArrangedSubview(button) + } + } + + private func setupSubtitleButtons() { + leadingSubtitleButton.setTitle(viewModel.leadingSubtitle, for: .normal) + leadingSubtitleButton.backgroundColor = .clear + leadingSubtitleButton.setTitleColor(.linkBlue(), for: .normal) + leadingSubtitleButton.translatesAutoresizingMaskIntoConstraints = false + leadingSubtitleButton.addTarget(self, action: #selector(leadingSubtitleButtonDidTap), for: .touchUpInside) + + trailingSubtitleButton.setTitle(viewModel.trailingSubtitle, for: .normal) + trailingSubtitleButton.backgroundColor = .clear + trailingSubtitleButton.setTitleColor(.linkBlue(), for: .normal) + trailingSubtitleButton.translatesAutoresizingMaskIntoConstraints = false + trailingSubtitleButton.addTarget(self, action: #selector(trailingSubtitleButtonDidTap), for: .touchUpInside) + } + + private func layout() { + view.addSubview(titleLabel) + view.addSubview(descriptionLabel) + view.addSubview(closeButton) + view.addSubview(stackView) + view.addSubview(leadingSubtitleButton) + view.addSubview(trailingSubtitleButton) + + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: closeButton.topAnchor), + titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: Constants.titleLeadingPadding), + titleLabel.trailingAnchor.constraint(equalTo: closeButton.leadingAnchor), + + descriptionLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: Constants.descriptionTopPadding), + descriptionLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor), + descriptionLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -Constants.titleTrailingPadding), + + closeButton.widthAnchor.constraint(equalToConstant: Constants.closeButtonSize), + closeButton.heightAnchor.constraint(equalToConstant: Constants.closeButtonSize), + closeButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: Constants.closeButtonTrailingPadding), + closeButton.topAnchor.constraint(equalTo: view.topAnchor, constant: Constants.closeButtonTopPadding), + + stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: Constants.titleLeadingPadding), + stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -Constants.titleLeadingPadding), + stackView.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: Constants.stackViewTopPadding), + + leadingSubtitleButton.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: Constants.subtitleButtonTopPadding), + leadingSubtitleButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: Constants.titleLeadingPadding), + leadingSubtitleButton.trailingAnchor.constraint(equalTo: view.centerXAnchor), + leadingSubtitleButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: Constants.subtitleButtonBottomPadding), + + trailingSubtitleButton.topAnchor.constraint(equalTo: leadingSubtitleButton.topAnchor, constant: Constants.subtitleButtonTopPadding), + trailingSubtitleButton.leadingAnchor.constraint(equalTo: view.centerXAnchor), + trailingSubtitleButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -Constants.titleLeadingPadding), + trailingSubtitleButton.bottomAnchor.constraint(equalTo: leadingSubtitleButton.bottomAnchor) + ]) + } + + @objc private func closeButtonDidTap() { + viewModel.didClose(reason: .close) + hide() + } + + private func productButtonDidTap(_ product: Product) { + viewModel.didSelectProduct(product) + viewModel.didClose(reason: .selectProduct) + hide() + } + + @objc private func leadingSubtitleButtonDidTap() { + viewModel.didClose(reason: .alreadyDonated) + hide() + } + + @objc private func trailingSubtitleButtonDidTap() { + viewModel.didClose(reason: .remindLater) + hide() + } + + func hide() { + UIView.transition(with: view, duration: kDefaultAnimationDuration / 2, options: .transitionCrossDissolve) { + self.view.isHidden = true + } + } +} diff --git a/iphone/Maps/UI/PlacePage/Components/Products/ProductsViewModel.swift b/iphone/Maps/UI/PlacePage/Components/Products/ProductsViewModel.swift new file mode 100644 index 0000000000..fa63b682fe --- /dev/null +++ b/iphone/Maps/UI/PlacePage/Components/Products/ProductsViewModel.swift @@ -0,0 +1,24 @@ +struct ProductsViewModel { + private let productsManager: ProductsManager.Type + + let title: String = L("support_organic_maps") + let description: String + let leadingSubtitle: String = L("already_donated") + let trailingSubtitle: String = L("remind_me_later") + let products: [Product] + + init(manager: ProductsManager.Type, configuration: ProductsConfiguration) { + self.productsManager = manager + self.description = configuration.placePagePrompt + self.products = configuration.products + } + + func didSelectProduct(_ product: Product) { + UIViewController.topViewController().openUrl(product.link, externally: true) + productsManager.didSelect(product) + } + + func didClose(reason: ProductsPopupCloseReason) { + productsManager.didCloseProductsPopup(with: reason) + } +} diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageCommonLayout.swift b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageCommonLayout.swift index ba4ebfd4a4..f71abcb560 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageCommonLayout.swift +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageCommonLayout.swift @@ -57,6 +57,13 @@ class PlacePageCommonLayout: NSObject, IPlacePageLayout { return vc } () + private func productsViewController() -> ProductsViewController? { + let productsManager = FrameworkHelper.self + guard let configuration = productsManager.getProductsConfiguration() else { return nil } + let viewModel = ProductsViewModel(manager: productsManager, configuration: configuration) + return ProductsViewController(viewModel: viewModel) + } + lazy var buttonsViewController: PlacePageButtonsViewController = { let vc = storyboard.instantiateViewController(ofType: PlacePageButtonsViewController.self) vc.buttonsData = placePageData.buttonsData! @@ -85,6 +92,7 @@ class PlacePageCommonLayout: NSObject, IPlacePageLayout { private func configureViewControllers() -> [UIViewController] { var viewControllers = [UIViewController]() + viewControllers.append(wikiDescriptionViewController) if let wikiDescriptionHtml = placePageData.wikiDescriptionHtml { wikiDescriptionViewController.descriptionHtml = wikiDescriptionHtml @@ -103,6 +111,10 @@ class PlacePageCommonLayout: NSObject, IPlacePageLayout { viewControllers.append(infoViewController) } + if let productsViewController = productsViewController() { + viewControllers.append(productsViewController) + } + if placePageData.buttonsData != nil { viewControllers.append(buttonsViewController) }