| import Delivery_Club_Models |
| import Delivery_Club_Analytics |
| import Delivery_Club_Managers |
| import Delivery_Club_VIP |
| import Delivery_Club_Remote_Config |
| private enum Constants { |
| static let defaultZoom = Float(15) |
| static let defaultRadius = 1200 |
| static let throttleDelay = 500 |
| static let maximumNumberOfVendors = 50 |
| } |
| final class MapInteractor<MapRestaurantsManager: RestaurantOnMapManagerProtocol> { |
| private let restaurantsManager: RestaurantOnMapManagerProtocol |
| private let throttler: ThrottlerProtocol |
| private let router: MapRouterProtocol |
| private let presenter: MapPresenterProtocol |
| private let analytics: MapAnalyticsAdapterProtocol |
| private let accountManager: DCAccountManager |
| private let locationPermissionFlow: LocationPermissionFlow |
| private let deviceLocationProvider: DeviceLocationProviderProtocol |
| private let remoteConfig: RemoteFeaturesDataProtocol |
| private let input: MapInput |
| private var restaurants: [RestaurantOnMap] = [] |
| private var zoom: Float = Constants.defaultZoom |
| private var isGeoAvailable: Bool { |
| remoteConfig.dev(.takeawayMapGeo) |
| } |
| private var userPosition: Location { |
| guard isGeoAvailable, let coordinate = deviceLocationProvider.deviceLocationIfAllowed?.coordinate else { |
| return input.userPosition |
| } |
| return Location(latitude: coordinate.latitude, longitude: coordinate.longitude) |
| } |
| init( |
| restaurantsManager: RestaurantOnMapManagerProtocol, |
| throttler: ThrottlerProtocol, |
| router: MapRouterProtocol, |
| presenter: MapPresenterProtocol, |
| analytics: MapAnalyticsAdapterProtocol, |
| accountManager: DCAccountManager, |
| locationPermissionFlow: LocationPermissionFlow, |
| deviceLocationProvider: DeviceLocationProviderProtocol, |
| remoteConfig: RemoteFeaturesDataProtocol, |
| input: MapInput) { |
| self.restaurantsManager = restaurantsManager |
| self.throttler = throttler |
| self.router = router |
| self.presenter = presenter |
| self.analytics = analytics |
| self.accountManager = accountManager |
| self.locationPermissionFlow = locationPermissionFlow |
| self.deviceLocationProvider = deviceLocationProvider |
| self.remoteConfig = remoteConfig |
| self.input = input |
| } |
| private func selectRestaurant(at indexToSelect: Int?) { |
| restaurants.indices.forEach { index in |
| restaurants[index].isSelected = index == indexToSelect |
| } |
| } |
| private func loadRestaurants(for position: Location, radius: Int, shouldSendAnalytics: Bool) { |
| let parameters = RestaurantOnMapRequest.Parameters( |
| lat: position.latitude, |
| long: position.longitude, |
| radius: radius) |
| let request = RestaurantOnMapRequest(parameters: parameters) |
| restaurantsManager.getRestaurantsOnMap(request) { [weak self] result in |
| guard let self = self else { return } |
| switch result { |
| case .success(let restaurants): |
| let restaurantsToShow = Array( |
| restaurants |
| .filter { $0.opened } |
| .prefix(Constants.maximumNumberOfVendors)) |
| let selectedVendorId = self.restaurants.first { $0.isSelected }?.vendorId |
| self.restaurants = restaurantsToShow.map { |
| var service = $0 |
| if service.vendorId == selectedVendorId { |
| service.isSelected = true |
| } |
| return service |
| } |
| self.presenter.presentRestaurants( |
| self.restaurants, |
| userPosition: self.userPosition, |
| zoom: self.zoom, |
| shouldSendAnalytics: shouldSendAnalytics) |
| if shouldSendAnalytics { |
| self.analytics.trackMapTagComplete( |
| availableVendors: self.restaurants.count, |
| source: "", |
| carouselName: "", |
| fastFilterList: []) |
| } |
| case .failure: |
| break |
| } |
| } |
| } |
| } |
| extension MapInteractor: MapInteractorProtocol { |
| func viewDidLoad() { |
| presenter.presentMap(for: input.userPosition, zoom: zoom) |
| loadRestaurants(for: input.userPosition, radius: Constants.defaultRadius, shouldSendAnalytics: true) |
| guard isGeoAvailable else { return } |
| locationPermissionFlow.startFlow { [weak self] isPermissionGranted in |
| self?.presenter.presentGeo(isPermissionGranted) |
| } |
| } |
| func mapGeoIsUnavailable() { |
| deviceLocationProvider.requestDeviceLocation { [weak self] result in |
| switch result { |
| case .success(let location): |
| let position = Location( |
| latitude: location.coordinate.latitude, |
| longitude: location.coordinate.longitude |
| ) |
| self?.presenter.centerMap(on: position) |
| case .failure: |
| break |
| } |
| } |
| } |
| func didTapBackButton() { |
| router.dismiss() |
| } |
| func didTapGeoUnavailableButton() { |
| locationPermissionFlow.askForPermission { [weak self] isPermissionGranted in |
| self?.presenter.presentGeo(isPermissionGranted) |
| } |
| } |
| func didSelectPin(with id: Int, pinsOnScreenCount: Int) { |
| guard let index = restaurants.firstIndex(where: { $0.vendorId == id }) else { return } |
| selectRestaurant(at: index) |
| presenter.presentRestaurants( |
| restaurants, |
| userPosition: userPosition, |
| zoom: zoom, |
| shouldSendAnalytics: false |
| ) |
| trackPinClick(at: index, availableVendors: pinsOnScreenCount) |
| } |
| func didSelectRestaurant(serviceId: Int, vendorId: Int) { |
| let entryPoint = AnalyticEntryPoint( |
| source: DC_AMP_VALUE_MAP_TAG, |
| carouselName: input.entryPoint.carouselName, |
| fastFilterList: input.entryPoint.fastFilterList |
| ) |
| router.openRestaurant(serviceId, vendorId: vendorId, deliveryType: .takeAway, entryPoint: entryPoint) |
| guard let index = restaurants.firstIndex(where: { $0.vendorId == vendorId }) else { return } |
| trackVendorClick(at: index) |
| } |
| func didMove(to position: Location, zoom: Float, radius: Int) { |
| presenter.presentRestaurantsIfNeeded( |
| restaurants, |
| userPosition: userPosition, |
| zoomBefore: self.zoom, |
| zoomAfter: zoom |
| ) |
| self.zoom = zoom |
| throttler.throttle(delay: .milliseconds(Constants.throttleDelay)) { [weak self] in |
| self?.loadRestaurants(for: position, radius: radius, shouldSendAnalytics: false) |
| } |
| } |
| func didReload(pinsOnScreenCount: Int) { |
| trackMapTagComplete(availableVendors: pinsOnScreenCount) |
| } |
| } |
| extension MapInteractor { |
| private func trackMapTagComplete(availableVendors: Int) { |
| analytics.trackMapTagComplete( |
| availableVendors: availableVendors, |
| source: input.entryPoint.source ?? DC_AMP_VALUE_CATALOG, |
| carouselName: input.entryPoint.carouselName, |
| fastFilterList: input.entryPoint.fastFilterList |
| ) |
| } |
| private func trackPinClick(at index: Int, availableVendors: Int) { |
| guard let restaurant = restaurants[safe: index] else { return } |
| analytics.trackPinClick( |
| availableVendors: availableVendors, |
| isVendorActive: restaurant.description.opened, |
| source: input.entryPoint.source ?? DC_AMP_VALUE_CATALOG, |
| carouselName: input.entryPoint.carouselName, |
| fastFilterList: input.entryPoint.fastFilterList |
| ) |
| } |
| private func trackVendorClick(at index: Int) { |
| guard let restaurant = restaurants[safe: index] else { return } |
| analytics.trackVendorClick( |
| restaurant: restaurant, |
| position: index, |
| isAuthorized: accountManager.isAuth(), |
| carouselName: input.entryPoint.carouselName, |
| isFromCarouselPage: input.entryPoint.carouselName != nil, |
| source: DC_AMP_VALUE_MAP_TAG, |
| filters: input.entryPoint.fastFilterList |
| ) |
| } |
| } |
Комментарии