From 354fa3067979ec2b0ee79c7ec4cb32fcca6fbc93 Mon Sep 17 00:00:00 2001 From: Ilya Grechuhin Date: Mon, 19 Feb 2018 17:06:17 +0300 Subject: [PATCH] [MAPSME-6511] [ios] Added new bookmarks categories interface. --- .../Actions/BMCActionsCreateCell.swift | 29 ++ .../Actions/BMCActionsCreateCell.xib | 63 +++++ .../Categories/BMCActionsCreateCell.xib | 67 +++++ .../Maps/Bookmarks/Categories/BMCModels.swift | 74 +++++ .../Categories/BMCNoBookmarksCell.swift | 2 + .../BMCView/BMCViewController.swift | 254 ++++++++++++++++++ .../Categories/BMCView/BMCViewController.xib | 199 ++++++++++++++ .../BMCViewModel/BMCDefaultViewModel.swift | 177 ++++++++++++ .../BMCViewModel/BMCViewModel.swift | 30 +++ .../BookmarksCategoriesDefaultViewModel.swift | 27 ++ ...ookmarksCategoriesBackupAndRestoreCell.xib | 70 +++++ .../Categories/BMCCategoriesHeader.swift | 32 +++ .../Categories/BMCCategoryCell.swift | 71 +++++ .../Categories/Categories/BMCCategoryCell.xib | 78 ++++++ .../Notifications/BMCNotificationsCell.swift | 18 ++ .../Notifications/BMCNotificationsCell.xib | 54 ++++ .../BMCNotificationsHeader.swift | 9 + .../Permissions/BMCPermissionsCell.swift | 51 ++++ .../Permissions/BMCPermissionsCell.xib | 58 ++++ .../Permissions/BMCPermissionsHeader.swift | 39 +++ .../BMCPermissionsPendingCell.swift | 54 ++++ .../Permissions/BMCPermissionsPendingCell.xib | 58 ++++ iphone/Maps/Bridging-Header.h | 3 + iphone/Maps/Classes/MapViewController.mm | 5 +- iphone/Maps/Maps.xcodeproj/project.pbxproj | 135 +++++++++- 25 files changed, 1651 insertions(+), 6 deletions(-) create mode 100644 iphone/Maps/Bookmarks/Categories/Actions/BMCActionsCreateCell.swift create mode 100644 iphone/Maps/Bookmarks/Categories/Actions/BMCActionsCreateCell.xib create mode 100644 iphone/Maps/Bookmarks/Categories/BMCActionsCreateCell.xib create mode 100644 iphone/Maps/Bookmarks/Categories/BMCModels.swift create mode 100644 iphone/Maps/Bookmarks/Categories/BMCNoBookmarksCell.swift create mode 100644 iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.swift create mode 100644 iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.xib create mode 100644 iphone/Maps/Bookmarks/Categories/BMCViewModel/BMCDefaultViewModel.swift create mode 100644 iphone/Maps/Bookmarks/Categories/BMCViewModel/BMCViewModel.swift create mode 100644 iphone/Maps/Bookmarks/Categories/BMCViewModel/BookmarksCategoriesDefaultViewModel.swift create mode 100644 iphone/Maps/Bookmarks/Categories/BookmarksCategoriesBackupAndRestoreCell.xib create mode 100644 iphone/Maps/Bookmarks/Categories/Categories/BMCCategoriesHeader.swift create mode 100644 iphone/Maps/Bookmarks/Categories/Categories/BMCCategoryCell.swift create mode 100644 iphone/Maps/Bookmarks/Categories/Categories/BMCCategoryCell.xib create mode 100644 iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsCell.swift create mode 100644 iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsCell.xib create mode 100644 iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsHeader.swift create mode 100644 iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsCell.swift create mode 100644 iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsCell.xib create mode 100644 iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsHeader.swift create mode 100644 iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsPendingCell.swift create mode 100644 iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsPendingCell.xib diff --git a/iphone/Maps/Bookmarks/Categories/Actions/BMCActionsCreateCell.swift b/iphone/Maps/Bookmarks/Categories/Actions/BMCActionsCreateCell.swift new file mode 100644 index 0000000000..eed93aa50a --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Actions/BMCActionsCreateCell.swift @@ -0,0 +1,29 @@ +final class BMCActionsCreateCell: UITableViewCell { + @IBOutlet private weak var actionImage: UIImageView! { + didSet { + actionImage.tintColor = .linkBlue() + } + } + + @IBOutlet private weak var actionTitle: UILabel! { + didSet { + actionTitle.font = .regular16() + actionTitle.textColor = .blackPrimaryText() + } + } + + private var model: BMCAction! { + didSet { + switch model! { + case .create: + actionImage.image = #imageLiteral(resourceName: "ic24PxAddCopy") + actionTitle.text = L("bookmarks_create_new_group") + } + } + } + + func config(model: BMCAction) -> UITableViewCell { + self.model = model + return self + } +} diff --git a/iphone/Maps/Bookmarks/Categories/Actions/BMCActionsCreateCell.xib b/iphone/Maps/Bookmarks/Categories/Actions/BMCActionsCreateCell.xib new file mode 100644 index 0000000000..4e72a958b9 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Actions/BMCActionsCreateCell.xib @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/Bookmarks/Categories/BMCActionsCreateCell.xib b/iphone/Maps/Bookmarks/Categories/BMCActionsCreateCell.xib new file mode 100644 index 0000000000..17021c8328 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/BMCActionsCreateCell.xib @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/Bookmarks/Categories/BMCModels.swift b/iphone/Maps/Bookmarks/Categories/BMCModels.swift new file mode 100644 index 0000000000..ebfa236208 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/BMCModels.swift @@ -0,0 +1,74 @@ +enum BMCSection { + case permissions + case categories + case actions + case notifications +} + +protocol BMCModel { +} + +enum BMCPermission: BMCModel { + case signup + case backup + case restore(Date?) +} + +@objc +protocol BMCCategoryObserver { + func categoryUpdated() +} + +class BMCCategory: BMCModel, Equatable { + let identifier: MWMMarkGroupID + var title: String { + didSet { + notifyObservers() + } + } + + var count: UInt64 { + didSet { + notifyObservers() + } + } + + var isVisible: Bool { + didSet { + notifyObservers() + } + } + + init(identifier: MWMMarkGroupID = 0, title: String = "My places", count: UInt64 = 0, isVisible: Bool = true) { + self.identifier = identifier + self.title = title + self.count = count + self.isVisible = isVisible + } + + private let observers = NSHashTable.weakObjects() + + func addObserver(_ observer: BMCCategoryObserver) { + observers.add(observer) + } + + func removeObserver(_ observer: BMCCategoryObserver) { + observers.remove(observer) + } + + private func notifyObservers() { + observers.allObjects.forEach { $0.categoryUpdated() } + } + + static func ==(lhs: BMCCategory, rhs: BMCCategory) -> Bool { + return lhs.identifier == rhs.identifier && lhs.title == rhs.title && lhs.count == rhs.count && lhs.isVisible == rhs.isVisible + } +} + +enum BMCAction: BMCModel { + case create +} + +enum BMCNotification: BMCModel { + case load +} diff --git a/iphone/Maps/Bookmarks/Categories/BMCNoBookmarksCell.swift b/iphone/Maps/Bookmarks/Categories/BMCNoBookmarksCell.swift new file mode 100644 index 0000000000..44d9ce0a13 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/BMCNoBookmarksCell.swift @@ -0,0 +1,2 @@ +final class BMCEmtyDescriptionCell: UITableViewCell { +} diff --git a/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.swift b/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.swift new file mode 100644 index 0000000000..18b3e696fd --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.swift @@ -0,0 +1,254 @@ +final class BMCViewController: MWMViewController { + private var viewModel: BMCViewModel! { + didSet { + viewModel.view = self + tableView.dataSource = self + } + } + + @IBOutlet private weak var tableView: UITableView! { + didSet { + tableView.registerNibs(cells: [ + BMCPermissionsCell.self, + BMCPermissionsPendingCell.self, + BMCCategoryCell.self, + BMCActionsCreateCell.self, + BMCNotificationsCell.self, + ]) + } + } + + @IBOutlet private var permissionsHeader: BMCPermissionsHeader! { + didSet { + permissionsHeader.isCollapsed = false + permissionsHeader.delegate = self + } + } + + @IBOutlet private var categoriesHeader: BMCCategoriesHeader! { + didSet { + categoriesHeader.delegate = self + } + } + + @IBOutlet private var actionsHeader: UIView! + @IBOutlet private var notificationsHeader: BMCNotificationsHeader! + + override func viewDidLoad() { + super.viewDidLoad() + title = L("bookmarks") + viewModel = BMCDefaultViewModel() + } + + private func updateCategoryName(category: BMCCategory?) { + let isNewCategory = (category == nil) + let alert = UIAlertController(title: L(isNewCategory ? "bookmarks_create_new_group" : "rename"), message: nil, preferredStyle: .alert) + + alert.addTextField { textField in + textField.placeholder = L("bookmark_set_name") + if let category = category { + textField.text = category.title + } + } + + alert.addAction(UIAlertAction(title: L("cancel").capitalized, style: .cancel, handler: nil)) + alert.addAction(UIAlertAction(title: L(isNewCategory ? "create" : "ok").capitalized, style: .default) { [weak alert, viewModel] _ in + guard let categoryName = alert?.textFields?.first?.text, !categoryName.isEmpty, + let viewModel = viewModel else { return } + if let category = category { + viewModel.renameCategory(category: category, name: categoryName) + } else { + viewModel.addCategory(name: categoryName) + } + }) + + present(alert, animated: true, completion: nil) + } + + private func shareCategory(category: BMCCategory, anchor: UIView) { + let url = viewModel.beginShareCategory(category: category) + typealias AVC = MWMActivityViewController + let fileName = (url.lastPathComponent as NSString).deletingPathExtension + let message = String(coreFormat: L("share_bookmarks_email_body"), arguments: [fileName]) + let shareController = AVC.share(for: url, message: message) { [viewModel] _, _, _, _ in + viewModel?.endShareCategory(category: category) + } + shareController!.present(inParentViewController: self, anchorView: anchor) + } + + private func openCategory(category: BMCCategory) { + let bmViewController = BookmarksVC(category: category.identifier)! + navigationController!.pushViewController(bmViewController, animated: true) + } + + private func signup(anchor: UIView, onComplete: @escaping (Bool) -> Void) { + if MWMAuthorizationViewModel.hasSocialToken() { + MWMAuthorizationViewModel.checkAuthentication(with: .bookmarks, onComplete: onComplete) + } else { + let authVC = AuthorizationViewController(popoverSourceView: anchor, + permittedArrowDirections: .any, + sourceComponent: .bookmarks, + successHandler: { _ in onComplete(true) }, + errorHandler: { _ in onComplete(false) }, + completionHandler: { + $0.dismiss(animated: true, completion: nil) + }) + present(authVC, animated: true, completion: nil) + } + } + + private func editCategory(category: BMCCategory, anchor: UIView) { + let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + + let rename = L("rename").capitalized + actionSheet.addAction(UIAlertAction(title: rename, style: .default, handler: { _ in + self.updateCategoryName(category: category) + })) + let showHide = L(category.isVisible ? "hide" : "show").capitalized + actionSheet.addAction(UIAlertAction(title: showHide, style: .default, handler: { _ in + self.visibilityAction(category: category) + })) + let share = L("share").capitalized + actionSheet.addAction(UIAlertAction(title: share, style: .default, handler: { _ in + self.shareCategory(category: category, anchor: anchor) + })) + let delete = L("delete").capitalized + let deleteAction = UIAlertAction(title: delete, style: .destructive, handler: { [viewModel] _ in + viewModel!.deleteCategory(category: category) + }) + deleteAction.isEnabled = (viewModel.numberOfRows(section: .categories) > 1) + actionSheet.addAction(deleteAction) + let cancel = L("cancel").capitalized + actionSheet.addAction(UIAlertAction(title: cancel, style: .cancel, handler: nil)) + + present(actionSheet, animated: true, completion: nil) + } +} + +extension BMCViewController: BMCView { + func update(sections: [BMCSection]) { + if sections.isEmpty { + tableView.reloadData() + } else { + let indexes = IndexSet(sections.map { viewModel.sectionIndex(section: $0) }) + tableView.update { tableView.reloadSections(indexes, with: .automatic) } + } + } +} + +extension BMCViewController: UITableViewDataSource { + func numberOfSections(in _: UITableView) -> Int { + return viewModel.numberOfSections() + } + + func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int { + switch viewModel.sectionType(section: section) { + case .permissions: + return permissionsHeader.isCollapsed ? 0 : viewModel.numberOfRows(section: section) + case .categories: fallthrough + case .actions: fallthrough + case .notifications: return viewModel.numberOfRows(section: section) + } + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + func dequeCell(_ cell: Cell.Type) -> Cell where Cell: UITableViewCell { + return tableView.dequeueReusableCell(cell: cell, indexPath: indexPath) + } + switch viewModel.item(indexPath: indexPath) { + case let permission as BMCPermission: + if viewModel.isPendingPermission { + return dequeCell(BMCPermissionsPendingCell.self).config(permission: permission) + } else { + return dequeCell(BMCPermissionsCell.self).config(permission: permission, delegate: self) + } + case let category as BMCCategory: + return dequeCell(BMCCategoryCell.self).config(category: category, delegate: self) + case let model as BMCAction: + return dequeCell(BMCActionsCreateCell.self).config(model: model) + case is BMCNotification: + return dequeCell(BMCNotificationsCell.self) + default: + assertionFailure() + return UITableViewCell() + } + } +} + +extension BMCViewController: UITableViewDelegate { + func tableView(_: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + switch viewModel.sectionType(section: section) { + case .permissions: fallthrough + case .notifications: fallthrough + case .categories: return 48 + case .actions: return 24 + } + } + + func tableView(_: UITableView, viewForHeaderInSection section: Int) -> UIView? { + switch viewModel.sectionType(section: section) { + case .permissions: return permissionsHeader + case .categories: + categoriesHeader.isShowAll = !viewModel.areAllCategoriesVisible() + return categoriesHeader + case .actions: return actionsHeader + case .notifications: return notificationsHeader + } + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + switch viewModel.item(indexPath: indexPath) { + case is BMCPermission: + return + case let category as BMCCategory: + openCategory(category: category) + case let action as BMCAction: + switch action { + case .create: updateCategoryName(category: nil) + } + default: + assertionFailure() + } + } +} + +extension BMCViewController: BMCPermissionsCellDelegate { + func permissionAction(permission: BMCPermission, anchor: UIView) { + switch permission { + case .signup: + viewModel.pendingPermission(isPending: true) + signup(anchor: anchor, onComplete: { [viewModel] success in + viewModel!.grant(permission: success ? .backup : nil) + }) + case .backup: + viewModel.grant(permission: permission) + case .restore: assertionFailure() + } + } +} + +extension BMCViewController: BMCCategoryCellDelegate { + func visibilityAction(category: BMCCategory) { + viewModel.updateCategoryVisibility(category: category) + categoriesHeader.isShowAll = !viewModel.areAllCategoriesVisible() + } + + func moreAction(category: BMCCategory, anchor: UIView) { + editCategory(category: category, anchor: anchor) + } +} + +extension BMCViewController: BMCPermissionsHeaderDelegate { + func collapseAction(isCollapsed: Bool) { + permissionsHeader.isCollapsed = !isCollapsed + update(sections: [.permissions]) + } +} + +extension BMCViewController: BMCCategoriesHeaderDelegate { + func visibilityAction(isShowAll: Bool) { + viewModel.updateAllCategoriesVisibility(isShowAll: isShowAll) + categoriesHeader.isShowAll = !viewModel.areAllCategoriesVisible() + } +} diff --git a/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.xib b/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.xib new file mode 100644 index 0000000000..1ac8718a83 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.xib @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/Bookmarks/Categories/BMCViewModel/BMCDefaultViewModel.swift b/iphone/Maps/Bookmarks/Categories/BMCViewModel/BMCDefaultViewModel.swift new file mode 100644 index 0000000000..bbd038965a --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/BMCViewModel/BMCDefaultViewModel.swift @@ -0,0 +1,177 @@ +final class BMCDefaultViewModel: NSObject { + var view: BMCView! + + private var sections: [BMCSection] = [] + private var permissions: [BMCPermission] = [] + private var categories: [BMCCategory] = [] + private var actions: [BMCAction] = [] + private var notifications: [BMCNotification] = [] + + private(set) var isPendingPermission = false + private var isAuthenticated = false + + typealias BM = MWMBookmarksManager + + override init() { + super.init() + MWMBookmarksManager.add(self) + loadData() + } + + private func setPermissions() { + isAuthenticated = MWMAuthorizationViewModel.isAuthenticated() + if !isAuthenticated { + Statistics.logEvent(kStatBookmarksSyncProposalShown, withParameters: [kStatHasAuthorization: 0]) + permissions = [.signup] + } else if !BM.isCloudEnabled() { + Statistics.logEvent(kStatBookmarksSyncProposalShown, withParameters: [kStatHasAuthorization: 1]) + isPendingPermission = false + permissions = [.backup] + } else { + isPendingPermission = true + permissions = [.restore(BM.lastSynchronizationDate())] + } + } + + private func setCategories() { + categories = BM.categoriesIdList().map { categoryId -> BMCCategory in + let title = BM.getCategoryName(categoryId)! + let count = BM.getCategoryMarksCount(categoryId) + BM.getCategoryTracksCount(categoryId) + let isVisible = BM.isCategoryVisible(categoryId) + return BMCCategory(identifier: categoryId, title: title, count: count, isVisible: isVisible) + } + } + + private func setActions() { + actions = [.create] + } + + private func setNotifications() { + notifications.append(.load) + } + + private func loadData() { + sections = [] + + sections.append(.permissions) + setPermissions() + + if MWMBookmarksManager.areBookmarksLoaded() { + sections.append(.categories) + setCategories() + + sections.append(.actions) + setActions() + } else { + sections.append(.notifications) + setNotifications() + } + view?.update(sections: []) + } +} + +extension BMCDefaultViewModel: BMCViewModel { + func numberOfSections() -> Int { + return sections.count + } + + func sectionType(section: Int) -> BMCSection { + return sections[section] + } + + func sectionIndex(section: BMCSection) -> Int { + return sections.index(of: section)! + } + + func numberOfRows(section: Int) -> Int { + return numberOfRows(section: sectionType(section: section)) + } + + func numberOfRows(section: BMCSection) -> Int { + switch section { + case .permissions: return permissions.count + case .categories: return categories.count + case .actions: return actions.count + case .notifications: return notifications.count + } + } + + func item(indexPath: IndexPath) -> BMCModel { + let (section, row) = (indexPath.section, indexPath.row) + switch sectionType(section: section) { + case .permissions: return permissions[row] + case .categories: return categories[row] + case .actions: return actions[row] + case .notifications: return notifications[row] + } + } + + func areAllCategoriesVisible() -> Bool { + return categories.reduce(true) { $0 && $1.isVisible } + } + + func updateAllCategoriesVisibility(isShowAll: Bool) { + categories.forEach { $0.isVisible = isShowAll } + BM.setAllCategoriesVisible(isShowAll) + } + + func updateCategoryVisibility(category: BMCCategory) { + category.isVisible = !category.isVisible + BM.setCategory(category.identifier, isVisible: category.isVisible) + } + + func addCategory(name: String) { + categories.append(BMCCategory(identifier: BM.createCategory(withName: name), title: name)) + view.update(sections: [.categories]) + } + + func renameCategory(category: BMCCategory, name: String) { + category.title = name + BM.setCategory(category.identifier, name: name) + } + + func deleteCategory(category: BMCCategory) { + categories.remove(at: categories.index(of: category)!) + BM.deleteCategory(category.identifier) + view.update(sections: [.categories]) + } + + func beginShareCategory(category: BMCCategory) -> URL { + return BM.beginShareCategory(category.identifier) + } + + func endShareCategory(category: BMCCategory) { + BM.endShareCategory(category.identifier) + } + + func pendingPermission(isPending: Bool) { + isPendingPermission = isPending + setPermissions() + view.update(sections: [.permissions]) + } + + func grant(permission: BMCPermission?) { + if let permission = permission { + switch permission { + case .signup: + assertionFailure() + case .backup: + Statistics.logEvent(kStatBookmarksSyncProposalApproved, + withParameters: [ + kStatNetwork: Statistics.connectionTypeString(), + kStatHasAuthorization: isAuthenticated ? 1 : 0, + ]) + BM.setCloudEnabled(true) + case .restore: + assertionFailure("Not implemented") + } + } + pendingPermission(isPending: false) + } +} + +extension BMCDefaultViewModel: MWMBookmarksObserver { + func onBookmarksLoadFinished() { + loadData() + } +} diff --git a/iphone/Maps/Bookmarks/Categories/BMCViewModel/BMCViewModel.swift b/iphone/Maps/Bookmarks/Categories/BMCViewModel/BMCViewModel.swift new file mode 100644 index 0000000000..890b8137c5 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/BMCViewModel/BMCViewModel.swift @@ -0,0 +1,30 @@ +protocol BMCView: class { + func update(sections: [BMCSection]) +} + +protocol BMCViewModel: class { + var view: BMCView! { get set } + var isPendingPermission: Bool { get } + + func numberOfSections() -> Int + func sectionType(section: Int) -> BMCSection + func sectionIndex(section: BMCSection) -> Int + func numberOfRows(section: Int) -> Int + func numberOfRows(section: BMCSection) -> Int + + func item(indexPath: IndexPath) -> BMCModel + + func areAllCategoriesVisible() -> Bool + func updateAllCategoriesVisibility(isShowAll: Bool) + func updateCategoryVisibility(category: BMCCategory) + + func addCategory(name: String) + func renameCategory(category: BMCCategory, name: String) + func deleteCategory(category: BMCCategory) + + func beginShareCategory(category: BMCCategory) -> URL + func endShareCategory(category: BMCCategory) + + func pendingPermission(isPending: Bool) + func grant(permission: BMCPermission?) +} diff --git a/iphone/Maps/Bookmarks/Categories/BMCViewModel/BookmarksCategoriesDefaultViewModel.swift b/iphone/Maps/Bookmarks/Categories/BMCViewModel/BookmarksCategoriesDefaultViewModel.swift new file mode 100644 index 0000000000..5e30b89f93 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/BMCViewModel/BookmarksCategoriesDefaultViewModel.swift @@ -0,0 +1,27 @@ +struct BMCDefaultViewModel: BMCViewModel { + private var sections: [BMCSection] = [.permissions, .categoriesList, .creation] + + func numberOfSections() -> Int { + return sections.count + } + + func sectionType(section: Int) -> BMCSection { + return sections[section] + } + + func sectionIndex(section: BMCSection) -> Int { + return sections.index(of: section)! + } + + func numberOfRows(section _: Int) -> Int { + return 1 + } + + func item(indexPath: IndexPath) -> BMCModel { + switch sectionType(section: indexPath.section) { + case .permissions: return BMCPermission.signup + case .categoriesList: return BMCCategory() + case .creation: return BMCAction.create + } + } +} diff --git a/iphone/Maps/Bookmarks/Categories/BookmarksCategoriesBackupAndRestoreCell.xib b/iphone/Maps/Bookmarks/Categories/BookmarksCategoriesBackupAndRestoreCell.xib new file mode 100644 index 0000000000..9b9890a4b9 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/BookmarksCategoriesBackupAndRestoreCell.xib @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/Bookmarks/Categories/Categories/BMCCategoriesHeader.swift b/iphone/Maps/Bookmarks/Categories/Categories/BMCCategoriesHeader.swift new file mode 100644 index 0000000000..029d297db1 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Categories/BMCCategoriesHeader.swift @@ -0,0 +1,32 @@ +protocol BMCCategoriesHeaderDelegate { + func visibilityAction(isShowAll: Bool) +} + +final class BMCCategoriesHeader: UIView { + @IBOutlet private weak var label: UILabel! { + didSet { + label.font = .bold14() + label.textColor = .blackSecondaryText() + label.text = L("bookmarks_groups").uppercased() + } + } + + @IBOutlet private weak var button: UIButton! { + didSet { + button.setTitleColor(.linkBlue(), for: .normal) + } + } + + var isShowAll = false { + didSet { + let title = L(isShowAll ? "bookmarks_groups_show_all" : "bookmarks_groups_hide_all") + button.setTitle(title, for: .normal) + } + } + + var delegate: BMCCategoriesHeaderDelegate! + + @IBAction private func buttonAction() { + delegate.visibilityAction(isShowAll: isShowAll) + } +} diff --git a/iphone/Maps/Bookmarks/Categories/Categories/BMCCategoryCell.swift b/iphone/Maps/Bookmarks/Categories/Categories/BMCCategoryCell.swift new file mode 100644 index 0000000000..ec75d2724d --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Categories/BMCCategoryCell.swift @@ -0,0 +1,71 @@ +protocol BMCCategoryCellDelegate { + func visibilityAction(category: BMCCategory) + func moreAction(category: BMCCategory, anchor: UIView) +} + +final class BMCCategoryCell: UITableViewCell { + @IBOutlet private weak var visibility: UIButton! + @IBOutlet private weak var title: UILabel! { + didSet { + title.font = .regular16() + title.textColor = .blackPrimaryText() + } + } + + @IBOutlet private weak var count: UILabel! { + didSet { + count.font = .regular14() + count.textColor = .blackSecondaryText() + } + } + + @IBOutlet private weak var more: UIButton! { + didSet { + more.tintColor = .blackSecondaryText() + more.setImage(#imageLiteral(resourceName: "ic24PxMore"), for: .normal) + } + } + + private var category: BMCCategory! { + willSet { + category?.removeObserver(self) + } + didSet { + categoryUpdated() + category?.addObserver(self) + } + } + + private var delegate: BMCCategoryCellDelegate! + + func config(category: BMCCategory, delegate: BMCCategoryCellDelegate) -> UITableViewCell { + self.category = category + self.delegate = delegate + return self + } + + @IBAction private func visibilityAction() { + delegate.visibilityAction(category: category) + } + + @IBAction private func moreAction() { + delegate.moreAction(category: category, anchor: more) + } +} + +extension BMCCategoryCell: BMCCategoryObserver { + func categoryUpdated() { + title.text = category.title + count.text = String(coreFormat: L("%s bookmarks_places"), arguments: [category.count]) + + if category.isVisible { + visibility.tintColor = .linkBlue() + visibility.setImage(#imageLiteral(resourceName: "radioBtnOn"), for: .normal) + visibility.imageView?.mwm_coloring = .blue + } else { + visibility.tintColor = .blackHintText() + visibility.setImage(#imageLiteral(resourceName: "radioBtnOff"), for: .normal) + visibility.imageView?.mwm_coloring = .gray + } + } +} diff --git a/iphone/Maps/Bookmarks/Categories/Categories/BMCCategoryCell.xib b/iphone/Maps/Bookmarks/Categories/Categories/BMCCategoryCell.xib new file mode 100644 index 0000000000..518f7f2116 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Categories/BMCCategoryCell.xib @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsCell.swift b/iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsCell.swift new file mode 100644 index 0000000000..0eb7f29540 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsCell.swift @@ -0,0 +1,18 @@ +final class BMCNotificationsCell: UITableViewCell { + @IBOutlet private weak var spinner: UIView! { + didSet { + circularProgress = MWMCircularProgress(parentView: spinner) + circularProgress.state = .spinner + } + } + + @IBOutlet private weak var label: UILabel! { + didSet { + label.text = L("load_kmz_title") + label.font = .regular16() + label.textColor = .blackPrimaryText() + } + } + + private var circularProgress: MWMCircularProgress! +} diff --git a/iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsCell.xib b/iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsCell.xib new file mode 100644 index 0000000000..42c1b1994a --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsCell.xib @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsHeader.swift b/iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsHeader.swift new file mode 100644 index 0000000000..8d04a3e286 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Notifications/BMCNotificationsHeader.swift @@ -0,0 +1,9 @@ +final class BMCNotificationsHeader: UIView { + @IBOutlet private weak var label: UILabel! { + didSet { + label.font = .bold14() + label.textColor = .blackSecondaryText() + label.text = L("bookmarks_groups").uppercased() + } + } +} diff --git a/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsCell.swift b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsCell.swift new file mode 100644 index 0000000000..fa51b8e273 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsCell.swift @@ -0,0 +1,51 @@ +protocol BMCPermissionsCellDelegate { + func permissionAction(permission: BMCPermission, anchor: UIView) +} + +final class BMCPermissionsCell: MWMTableViewCell { + @IBOutlet private weak var label: UILabel! { + didSet { + label.font = .regular14() + label.textColor = .blackSecondaryText() + } + } + + @IBOutlet private weak var button: UIButton! { + didSet { + button.setTitleColor(.white, for: .normal) + button.setBackgroundColor(.linkBlue(), for: .normal) + button.setBackgroundColor(.linkBlueHighlighted(), for: .highlighted) + button.titleLabel?.font = .regular14() + button.layer.cornerRadius = 6 + button.clipsToBounds = true + } + } + + private var permission: BMCPermission! { + didSet { + switch permission! { + case .signup: + label.text = L("bookmarks_message_unauthorized_user") + button.setTitle(L("authorization_button_sign_in").uppercased(), for: .normal) + case .backup: + label.text = L("bookmarks_message_authorized_user") + button.setTitle(L("bookmarks_backup").uppercased(), for: .normal) + case .restore: assertionFailure() + } + } + } + + private var delegate: BMCPermissionsCellDelegate! + + func config(permission: BMCPermission, delegate: BMCPermissionsCellDelegate) -> UITableViewCell { + self.permission = permission + self.delegate = delegate + isSeparatorHidden = true + backgroundColor = .clear + return self + } + + @IBAction private func buttonAction() { + delegate.permissionAction(permission: permission, anchor: button) + } +} diff --git a/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsCell.xib b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsCell.xib new file mode 100644 index 0000000000..554840ac87 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsCell.xib @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsHeader.swift b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsHeader.swift new file mode 100644 index 0000000000..51592c5996 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsHeader.swift @@ -0,0 +1,39 @@ +protocol BMCPermissionsHeaderDelegate { + func collapseAction(isCollapsed: Bool) +} + +final class BMCPermissionsHeader: UIView { + @IBOutlet private weak var label: UILabel! { + didSet { + label.font = .bold14() + label.textColor = .blackSecondaryText() + label.text = L("settings_backup_bookmarks").uppercased() + } + } + + @IBOutlet private weak var button: UIButton! { + didSet { + button.setImage(#imageLiteral(resourceName: "ic24PxChevronUp"), for: .normal) + button.tintColor = .blackSecondaryText() + updateButton() + } + } + + var isCollapsed = false { + didSet { + updateButton() + } + } + + var delegate: BMCPermissionsHeaderDelegate! + + private func updateButton() { + UIView.animate(withDuration: kDefaultAnimationDuration) { + self.button?.imageView?.transform = self.isCollapsed ? .init(rotationAngle: .pi) : .identity + } + } + + @IBAction private func buttonAction() { + delegate.collapseAction(isCollapsed: isCollapsed) + } +} diff --git a/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsPendingCell.swift b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsPendingCell.swift new file mode 100644 index 0000000000..42bce29634 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsPendingCell.swift @@ -0,0 +1,54 @@ +final class BMCPermissionsPendingCell: MWMTableViewCell { + @IBOutlet private weak var label: UILabel! { + didSet { + label.font = .regular14() + label.textColor = .blackSecondaryText() + } + } + + @IBOutlet private weak var spinner: UIView! + @IBOutlet private var spinnerBottom: NSLayoutConstraint! + + private var circularProgress: MWMCircularProgress! + + private var permission: BMCPermission! { + didSet { + switch permission! { + case .signup: + label.text = L("bookmarks_message_unauthorized_user") + startSpinner(start: true) + case .backup: assertionFailure() + case let .restore(date): + startSpinner(start: false) + if let date = date { + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .none + label.text = String(coreFormat: L("bookmarks_message_backuped_user %s"), arguments: [formatter.string(from: date)]) + } else { + label.text = L("bookmarks_message_unbackuped_user") + } + } + } + } + + private func startSpinner(start: Bool) { + if start { + circularProgress = MWMCircularProgress(parentView: spinner) + circularProgress.state = .spinner + spinnerBottom.isActive = true + spinner.isHidden = false + } else { + circularProgress = nil + spinnerBottom.isActive = false + spinner.isHidden = true + } + } + + func config(permission: BMCPermission) -> UITableViewCell { + self.permission = permission + isSeparatorHidden = true + backgroundColor = .clear + return self + } +} diff --git a/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsPendingCell.xib b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsPendingCell.xib new file mode 100644 index 0000000000..b95b38aaa2 --- /dev/null +++ b/iphone/Maps/Bookmarks/Categories/Permissions/BMCPermissionsPendingCell.xib @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/Maps/Bridging-Header.h b/iphone/Maps/Bridging-Header.h index 5997bc9f61..ff25fa8a6c 100644 --- a/iphone/Maps/Bridging-Header.h +++ b/iphone/Maps/Bridging-Header.h @@ -20,10 +20,13 @@ #import "private.h" #import "AppInfo.h" +#import "BookmarksVC.h" #import "LocalNotificationManager.h" +#import "MWMActivityViewController.h" #import "MWMAuthorizationViewModel.h" #import "MWMAvailableAreaAffectDirection.h" #import "MWMBanner.h" +#import "MWMBookmarksManager.h" #import "MWMBottomMenuViewController.h" #import "MWMCircularProgress+Swift.h" #import "MWMCollectionViewController.h" diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index d988cfb975..eadaeddd0f 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -1,5 +1,4 @@ #import "MapViewController.h" -#import "BookmarksRootVC.h" #import "BookmarksVC.h" #import "EAGLView.h" #import "MWMAPIBar.h" @@ -367,8 +366,8 @@ BOOL gIsFirstMyPositionMode = YES; { auto const & ids = GetFramework().GetBookmarkManager().GetBmGroupsIdList(); BOOL const oneCategory = (ids.size() == 1); - MWMTableViewController * vc = - oneCategory ? [[BookmarksVC alloc] initWithCategory:(ids.front())] : [[BookmarksRootVC alloc] init]; + auto vc = oneCategory ? [[BookmarksVC alloc] initWithCategory:(ids.front())] + : [[BMCViewController alloc] init]; [self.navigationController pushViewController:vc animated:YES]; } diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index 842ad17351..bac84a5893 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -36,6 +36,16 @@ 340475771E081A4600C92850 /* MWMTrafficManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 340475471E081A4600C92850 /* MWMTrafficManager.mm */; }; 3404757E1E081B3300C92850 /* iosOGLContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3404757A1E081B3300C92850 /* iosOGLContext.mm */; }; 340475811E081B3300C92850 /* iosOGLContextFactory.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3404757C1E081B3300C92850 /* iosOGLContextFactory.mm */; }; + 3404F48B202894EA0090E401 /* BMCViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404F489202894EA0090E401 /* BMCViewController.swift */; }; + 3404F48C202894EA0090E401 /* BMCViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3404F48A202894EA0090E401 /* BMCViewController.xib */; }; + 3404F48E2028966C0090E401 /* BMCViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404F48D2028966C0090E401 /* BMCViewModel.swift */; }; + 3404F490202898CC0090E401 /* BMCModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404F48F202898CC0090E401 /* BMCModels.swift */; }; + 3404F4952028A1B80090E401 /* BMCPermissionsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404F4932028A1B80090E401 /* BMCPermissionsCell.swift */; }; + 3404F4962028A1B80090E401 /* BMCPermissionsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3404F4942028A1B80090E401 /* BMCPermissionsCell.xib */; }; + 3404F4992028A20D0090E401 /* BMCCategoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404F4972028A20D0090E401 /* BMCCategoryCell.swift */; }; + 3404F49A2028A20D0090E401 /* BMCCategoryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3404F4982028A20D0090E401 /* BMCCategoryCell.xib */; }; + 3404F49D2028A2430090E401 /* BMCActionsCreateCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3404F49B2028A2430090E401 /* BMCActionsCreateCell.swift */; }; + 3404F49E2028A2430090E401 /* BMCActionsCreateCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3404F49C2028A2430090E401 /* BMCActionsCreateCell.xib */; }; 34065A111F45E7F8006684E5 /* GoogleFallbackBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34065A0F1F45E7F8006684E5 /* GoogleFallbackBanner.swift */; }; 3406FA161C6E0C3300E9FAD2 /* MWMMapDownloadDialog.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3406FA141C6E0C3300E9FAD2 /* MWMMapDownloadDialog.mm */; }; 3406FA191C6E0D8F00E9FAD2 /* MWMMapDownloadDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3406FA171C6E0D8F00E9FAD2 /* MWMMapDownloadDialog.xib */; }; @@ -51,6 +61,8 @@ 340E1EF81E2F614400CE49BF /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 340E1EE81E2F614400CE49BF /* Settings.storyboard */; }; 340E1EFB1E2F614400CE49BF /* Storyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340E1EE91E2F614400CE49BF /* Storyboard.swift */; }; 340E1EFE1E2F614400CE49BF /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 340E1EEA1E2F614400CE49BF /* Welcome.storyboard */; }; + 340FDC092031C39E00F140AD /* BMCPermissionsPendingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340FDC072031C39E00F140AD /* BMCPermissionsPendingCell.swift */; }; + 340FDC0A2031C39E00F140AD /* BMCPermissionsPendingCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 340FDC082031C39E00F140AD /* BMCPermissionsPendingCell.xib */; }; 3411387D1C15AE73002E3B3E /* libeditor.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3411387C1C15AE73002E3B3E /* libeditor.a */; }; 3411E7641F7CE5DF00A49FCD /* GoogleMobileAds.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3411E7621F7CE5DC00A49FCD /* GoogleMobileAds.framework */; }; 341F09841C20138100F18AC5 /* libpugixml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 341F09831C20138100F18AC5 /* libpugixml.a */; }; @@ -111,6 +123,9 @@ 3466A2D71FB1C83C005494D3 /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3466A2C71FB1C83B005494D3 /* Bolts.framework */; }; 3466A2DA1FB1C83C005494D3 /* FacebookCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3466A2C81FB1C83B005494D3 /* FacebookCore.framework */; }; 3466A2DD1FB1C83C005494D3 /* FacebookLogin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3466A2C91FB1C83B005494D3 /* FacebookLogin.framework */; }; + 3467CEB2202C6EEE00D3C670 /* BMCNotificationsHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3467CEB1202C6EEE00D3C670 /* BMCNotificationsHeader.swift */; }; + 3467CEB6202C6FA900D3C670 /* BMCNotificationsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3467CEB4202C6FA900D3C670 /* BMCNotificationsCell.swift */; }; + 3467CEB7202C6FA900D3C670 /* BMCNotificationsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3467CEB5202C6FA900D3C670 /* BMCNotificationsCell.xib */; }; 346B42AC1DD5E3D20094EBEE /* MWMLocationNotFoundAlert.mm in Sources */ = {isa = PBXBuildFile; fileRef = 346B42AA1DD5E3D20094EBEE /* MWMLocationNotFoundAlert.mm */; }; 346DB8281E5C4F6700E3123E /* GalleryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346DB81E1E5C4F6700E3123E /* GalleryCell.swift */; }; 346DB82B1E5C4F6700E3123E /* GalleryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 346DB81F1E5C4F6700E3123E /* GalleryCell.xib */; }; @@ -244,6 +259,9 @@ 34B6FD5F2015E6BF00C18E97 /* DiscoveryBookingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6FD5D2015E6BE00C18E97 /* DiscoveryBookingCell.swift */; }; 34B6FD602015E6BF00C18E97 /* DiscoveryBookingCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34B6FD5E2015E6BF00C18E97 /* DiscoveryBookingCell.xib */; }; 34B6FD622015F71A00C18E97 /* DiscoveryBookingCollectionHolderCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34B6FD612015F71900C18E97 /* DiscoveryBookingCollectionHolderCell.xib */; }; + 34B846A12029DCC10081ECCD /* BMCCategoriesHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B846A02029DCC10081ECCD /* BMCCategoriesHeader.swift */; }; + 34B846A32029DFEB0081ECCD /* BMCPermissionsHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B846A22029DFEB0081ECCD /* BMCPermissionsHeader.swift */; }; + 34B846A82029E8110081ECCD /* BMCDefaultViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B846A72029E8110081ECCD /* BMCDefaultViewModel.swift */; }; 34B924431DC8A29C0008D971 /* MWMMailViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34B924411DC8A29C0008D971 /* MWMMailViewController.mm */; }; 34BBD6471F82649D0070CA50 /* GoogleSignIn.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 34BBD6451F8264980070CA50 /* GoogleSignIn.bundle */; }; 34BBD64C1F826DB10070CA50 /* AuthorizationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BBD64A1F826DB10070CA50 /* AuthorizationViewController.swift */; }; @@ -739,6 +757,16 @@ 3404757A1E081B3300C92850 /* iosOGLContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = iosOGLContext.mm; sourceTree = ""; }; 3404757B1E081B3300C92850 /* iosOGLContextFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iosOGLContextFactory.h; sourceTree = ""; }; 3404757C1E081B3300C92850 /* iosOGLContextFactory.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = iosOGLContextFactory.mm; sourceTree = ""; }; + 3404F489202894EA0090E401 /* BMCViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCViewController.swift; sourceTree = ""; }; + 3404F48A202894EA0090E401 /* BMCViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BMCViewController.xib; sourceTree = ""; }; + 3404F48D2028966C0090E401 /* BMCViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCViewModel.swift; sourceTree = ""; }; + 3404F48F202898CC0090E401 /* BMCModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCModels.swift; sourceTree = ""; }; + 3404F4932028A1B80090E401 /* BMCPermissionsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCPermissionsCell.swift; sourceTree = ""; }; + 3404F4942028A1B80090E401 /* BMCPermissionsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BMCPermissionsCell.xib; sourceTree = ""; }; + 3404F4972028A20D0090E401 /* BMCCategoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCCategoryCell.swift; sourceTree = ""; }; + 3404F4982028A20D0090E401 /* BMCCategoryCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BMCCategoryCell.xib; sourceTree = ""; }; + 3404F49B2028A2430090E401 /* BMCActionsCreateCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCActionsCreateCell.swift; sourceTree = ""; }; + 3404F49C2028A2430090E401 /* BMCActionsCreateCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BMCActionsCreateCell.xib; sourceTree = ""; }; 340537621BBED98600D452C6 /* MWMMapViewControlsCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MWMMapViewControlsCommon.h; path = APIBar/MWMMapViewControlsCommon.h; sourceTree = ""; }; 34065A0F1F45E7F8006684E5 /* GoogleFallbackBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleFallbackBanner.swift; sourceTree = ""; }; 3406FA131C6E0C3300E9FAD2 /* MWMMapDownloadDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMMapDownloadDialog.h; sourceTree = ""; }; @@ -763,6 +791,8 @@ 340E1EE81E2F614400CE49BF /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; 340E1EE91E2F614400CE49BF /* Storyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storyboard.swift; sourceTree = ""; }; 340E1EEA1E2F614400CE49BF /* Welcome.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Welcome.storyboard; sourceTree = ""; }; + 340FDC072031C39E00F140AD /* BMCPermissionsPendingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCPermissionsPendingCell.swift; sourceTree = ""; }; + 340FDC082031C39E00F140AD /* BMCPermissionsPendingCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BMCPermissionsPendingCell.xib; sourceTree = ""; }; 3411387C1C15AE73002E3B3E /* libeditor.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libeditor.a; path = "../../../omim-xcode-build/Debug/libeditor.a"; sourceTree = ""; }; 3411E7621F7CE5DC00A49FCD /* GoogleMobileAds.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = GoogleMobileAds.framework; sourceTree = ""; }; 341522BD1B666A550077AA8F /* MWMAPIBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMAPIBarView.h; sourceTree = ""; }; @@ -854,6 +884,10 @@ 3466A2C71FB1C83B005494D3 /* Bolts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bolts.framework; path = Carthage/Build/iOS/Bolts.framework; sourceTree = ""; }; 3466A2C81FB1C83B005494D3 /* FacebookCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FacebookCore.framework; path = Carthage/Build/iOS/FacebookCore.framework; sourceTree = ""; }; 3466A2C91FB1C83B005494D3 /* FacebookLogin.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FacebookLogin.framework; path = Carthage/Build/iOS/FacebookLogin.framework; sourceTree = ""; }; + 3467CEB1202C6EEE00D3C670 /* BMCNotificationsHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCNotificationsHeader.swift; sourceTree = ""; }; + 3467CEB4202C6FA900D3C670 /* BMCNotificationsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCNotificationsCell.swift; sourceTree = ""; }; + 3467CEB5202C6FA900D3C670 /* BMCNotificationsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BMCNotificationsCell.xib; sourceTree = ""; }; + 3467CEB8202C70A300D3C670 /* MWMCircularProgress+Swift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MWMCircularProgress+Swift.h"; sourceTree = ""; }; 346B42A91DD5E3D20094EBEE /* MWMLocationNotFoundAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMLocationNotFoundAlert.h; sourceTree = ""; }; 346B42AA1DD5E3D20094EBEE /* MWMLocationNotFoundAlert.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMLocationNotFoundAlert.mm; sourceTree = ""; }; 346B42AD1DD5E5450094EBEE /* MWMDefaultAlert_Protected.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDefaultAlert_Protected.h; sourceTree = ""; }; @@ -1051,6 +1085,9 @@ 34B6FD5D2015E6BE00C18E97 /* DiscoveryBookingCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscoveryBookingCell.swift; sourceTree = ""; }; 34B6FD5E2015E6BF00C18E97 /* DiscoveryBookingCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DiscoveryBookingCell.xib; sourceTree = ""; }; 34B6FD612015F71900C18E97 /* DiscoveryBookingCollectionHolderCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DiscoveryBookingCollectionHolderCell.xib; sourceTree = ""; }; + 34B846A02029DCC10081ECCD /* BMCCategoriesHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCCategoriesHeader.swift; sourceTree = ""; }; + 34B846A22029DFEB0081ECCD /* BMCPermissionsHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCPermissionsHeader.swift; sourceTree = ""; }; + 34B846A72029E8110081ECCD /* BMCDefaultViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCDefaultViewModel.swift; sourceTree = ""; }; 34B924401DC8A29C0008D971 /* MWMMailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMMailViewController.h; sourceTree = ""; }; 34B924411DC8A29C0008D971 /* MWMMailViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMMailViewController.mm; sourceTree = ""; }; 34BBD6451F8264980070CA50 /* GoogleSignIn.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = GoogleSignIn.bundle; path = 3party/GoogleSignIn/GoogleSignIn.bundle; sourceTree = ""; }; @@ -2112,6 +2149,20 @@ path = Traffic; sourceTree = ""; }; + 3404F4A02028A6C00090E401 /* Categories */ = { + isa = PBXGroup; + children = ( + 343D7B6D202AF4CA007D56A8 /* Actions */, + 3404F48F202898CC0090E401 /* BMCModels.swift */, + 343D7B6F202AF7AC007D56A8 /* BMCView */, + 343D7B6E202AF715007D56A8 /* BMCViewModel */, + 343D7B6C202AF459007D56A8 /* Categories */, + 3467CEB3202C6F7F00D3C670 /* Notifications */, + 343D7B6B202AF283007D56A8 /* Permissions */, + ); + path = Categories; + sourceTree = ""; + }; 34065A0E1F45E7E1006684E5 /* Google */ = { isa = PBXGroup; children = ( @@ -2168,6 +2219,55 @@ path = Ads; sourceTree = ""; }; + 343D7B6B202AF283007D56A8 /* Permissions */ = { + isa = PBXGroup; + children = ( + 340FDC072031C39E00F140AD /* BMCPermissionsPendingCell.swift */, + 340FDC082031C39E00F140AD /* BMCPermissionsPendingCell.xib */, + 3404F4932028A1B80090E401 /* BMCPermissionsCell.swift */, + 3404F4942028A1B80090E401 /* BMCPermissionsCell.xib */, + 34B846A22029DFEB0081ECCD /* BMCPermissionsHeader.swift */, + ); + path = Permissions; + sourceTree = ""; + }; + 343D7B6C202AF459007D56A8 /* Categories */ = { + isa = PBXGroup; + children = ( + 34B846A02029DCC10081ECCD /* BMCCategoriesHeader.swift */, + 3404F4972028A20D0090E401 /* BMCCategoryCell.swift */, + 3404F4982028A20D0090E401 /* BMCCategoryCell.xib */, + ); + path = Categories; + sourceTree = ""; + }; + 343D7B6D202AF4CA007D56A8 /* Actions */ = { + isa = PBXGroup; + children = ( + 3404F49B2028A2430090E401 /* BMCActionsCreateCell.swift */, + 3404F49C2028A2430090E401 /* BMCActionsCreateCell.xib */, + ); + path = Actions; + sourceTree = ""; + }; + 343D7B6E202AF715007D56A8 /* BMCViewModel */ = { + isa = PBXGroup; + children = ( + 34B846A72029E8110081ECCD /* BMCDefaultViewModel.swift */, + 3404F48D2028966C0090E401 /* BMCViewModel.swift */, + ); + path = BMCViewModel; + sourceTree = ""; + }; + 343D7B6F202AF7AC007D56A8 /* BMCView */ = { + isa = PBXGroup; + children = ( + 3404F489202894EA0090E401 /* BMCViewController.swift */, + 3404F48A202894EA0090E401 /* BMCViewController.xib */, + ); + path = BMCView; + sourceTree = ""; + }; 343F5A7F1FB61012007DF002 /* Platform */ = { isa = PBXGroup; children = ( @@ -2212,6 +2312,7 @@ 3454D79D1E07F045004AF2AD /* DateComponentsFormatter+ETA.swift */, 3454D79E1E07F045004AF2AD /* NSString+Categories.h */, 3454D79F1E07F045004AF2AD /* NSString+Categories.mm */, + F6550C1D1FD81B3800352D88 /* RatingSummaryView+DefaultConfig.swift */, 3457C4241F680F1900028233 /* String+BoundingRect.swift */, 34763F051F3092E700F4D2D3 /* String+Format.swift */, 3454D7A01E07F045004AF2AD /* UIButton+Orientation.h */, @@ -2242,6 +2343,7 @@ 3454D7B31E07F045004AF2AD /* UITextField+RuntimeAttributes.mm */, 3454D7B41E07F045004AF2AD /* UITextView+RuntimeAttributes.h */, 3454D7B51E07F045004AF2AD /* UITextView+RuntimeAttributes.mm */, + 348B926B1FF3B5E100379009 /* UIView+Animation.swift */, 3404164A1E7BF42D00E2B6D6 /* UIView+Coordinates.swift */, 34F5E0D21E3F254800B1C415 /* UIView+Hierarchy.swift */, 3454D7B61E07F045004AF2AD /* UIView+RuntimeAttributes.h */, @@ -2251,8 +2353,6 @@ 34F7422F1E0834F400AC1FD6 /* UIViewController+Navigation.h */, 34F742301E0834F400AC1FD6 /* UIViewController+Navigation.mm */, 347E03981FAC5F1D00426032 /* UIWindow+InputLanguage.swift */, - F6550C1D1FD81B3800352D88 /* RatingSummaryView+DefaultConfig.swift */, - 348B926B1FF3B5E100379009 /* UIView+Animation.swift */, ); path = Categories; sourceTree = ""; @@ -2327,6 +2427,16 @@ path = APIBar; sourceTree = ""; }; + 3467CEB3202C6F7F00D3C670 /* Notifications */ = { + isa = PBXGroup; + children = ( + 3467CEB1202C6EEE00D3C670 /* BMCNotificationsHeader.swift */, + 3467CEB4202C6FA900D3C670 /* BMCNotificationsCell.swift */, + 3467CEB5202C6FA900D3C670 /* BMCNotificationsCell.xib */, + ); + path = Notifications; + sourceTree = ""; + }; 346B42A81DD5E3D20094EBEE /* LocationNotFoundAlert */ = { isa = PBXGroup; children = ( @@ -2525,9 +2635,10 @@ 349A35751B53D4C9009677EE /* MWMCircularProgress.h */, 349A35761B53D4C9009677EE /* MWMCircularProgress.mm */, 349A35771B53D4C9009677EE /* MWMCircularProgress.xib */, + 3467CEB8202C70A300D3C670 /* MWMCircularProgress+Swift.h */, + 340708891F2B8CBF00029ECC /* MWMCircularProgressState.h */, 349A35781B53D4C9009677EE /* MWMCircularProgressView.h */, 349A35791B53D4C9009677EE /* MWMCircularProgressView.mm */, - 340708891F2B8CBF00029ECC /* MWMCircularProgressState.h */, ); name = CircularProgress; path = CustomViews/CircularProgress; @@ -3936,6 +4047,7 @@ 6741A9531BF340DE002C974C /* 01_dejavusans.ttf in Resources */, 6741A9541BF340DE002C974C /* 02_droidsans-fallback.ttf in Resources */, 6741A9571BF340DE002C974C /* 03_jomolhari-id-a3d.ttf in Resources */, + 3404F49A2028A20D0090E401 /* BMCCategoryCell.xib in Resources */, 6741A9581BF340DE002C974C /* 04_padauk.ttf in Resources */, 6741A9591BF340DE002C974C /* 05_khmeros.ttf in Resources */, 34AB66801FC5AA330078E451 /* MWMiPhoneRoutePreview.xib in Resources */, @@ -3990,6 +4102,7 @@ F6B97B2A1CD0CB170009B612 /* MWMBookmarkNameCell.xib in Resources */, F6E2FD9B1E097BA00083EBEC /* MWMBookmarkTitleCell.xib in Resources */, 349D1AD21E2E325B004A2006 /* MWMBottomMenuCollectionViewLandscapeCell.xib in Resources */, + 3467CEB7202C6FA900D3C670 /* BMCNotificationsCell.xib in Resources */, 349D1AD51E2E325B004A2006 /* MWMBottomMenuCollectionViewPortraitCell.xib in Resources */, 349D1AE11E2E325C004A2006 /* MWMBottomMenuViewController.xib in Resources */, 34D3B01E1E389D05004100F9 /* MWMButtonCell.xib in Resources */, @@ -4013,6 +4126,7 @@ 34D3B03F1E389D05004100F9 /* MWMEditorSwitchTableViewCell.xib in Resources */, 34D3B0451E389D05004100F9 /* MWMEditorTextTableViewCell.xib in Resources */, F64D9CA31C899C760063FA30 /* MWMEditorViralAlert.xib in Resources */, + 340FDC0A2031C39E00F140AD /* BMCPermissionsPendingCell.xib in Resources */, 346DB8311E5C4F6700E3123E /* GalleryItemViewController.xib in Resources */, 6741A9911BF340DE002C974C /* MWMFacebookAlert.xib in Resources */, 6741A96D1BF340DE002C974C /* MWMLocationAlert.xib in Resources */, @@ -4022,6 +4136,7 @@ F6EBB2731FD7E4FD00B69B6A /* DiscoveryNoResultsCell.xib in Resources */, F6E2FD621E097BA00083EBEC /* MWMMapDownloaderLargeCountryTableViewCell.xib in Resources */, F6664BFD1E6459CB00E703C2 /* PPFacilityCell.xib in Resources */, + 3404F48C202894EA0090E401 /* BMCViewController.xib in Resources */, F6E2FD681E097BA00083EBEC /* MWMMapDownloaderPlaceTableViewCell.xib in Resources */, F6E2FD6E1E097BA00083EBEC /* MWMMapDownloaderSubplaceTableViewCell.xib in Resources */, 345E8F551F839E6C00A826CC /* GoogleService-Info.plist in Resources */, @@ -4078,6 +4193,7 @@ F6664C051E6459DA00E703C2 /* PPReviewHeaderCell.xib in Resources */, F6E2FF2A1E097BA00083EBEC /* MWMSearchTabButtonsView.xib in Resources */, BB25B1A71FB32767007276FA /* transit_colors.txt in Resources */, + 3404F4962028A1B80090E401 /* BMCPermissionsCell.xib in Resources */, F6E2FF421E097BA00083EBEC /* MWMSearchTableViewController.xib in Resources */, 34AB66681FC5AA330078E451 /* TransportTransitPedestrian.xib in Resources */, F6E2FEEE1E097BA00083EBEC /* MWMSearchView.xib in Resources */, @@ -4106,6 +4222,7 @@ 6741A9981BF340DE002C974C /* resources-xhdpi_clear in Resources */, 6741A9611BF340DE002C974C /* resources-xhdpi_dark in Resources */, 6741A94D1BF340DE002C974C /* resources-xxhdpi_clear in Resources */, + 3404F49E2028A2430090E401 /* BMCActionsCreateCell.xib in Resources */, 6741A9551BF340DE002C974C /* resources-xxhdpi_dark in Resources */, 340E1EF51E2F614400CE49BF /* SearchFilters.storyboard in Resources */, F682249F1E5B105900BC1C18 /* PPHotelDescriptionCell.xib in Resources */, @@ -4199,6 +4316,7 @@ 3472B5EF200F8F7600DC6CD5 /* BackgroundUGCUpload.swift in Sources */, 6741A9A81BF340DE002C974C /* MWMFacebookAlert.mm in Sources */, 34AB665F1FC5AA330078E451 /* TransportTransitIntermediatePoint.swift in Sources */, + 34B846A82029E8110081ECCD /* BMCDefaultViewModel.swift in Sources */, 348A8DF51F66775A00D83026 /* RatingView.swift in Sources */, F63AF50F1EA6215100A1DB98 /* FilterPriceCategoryCell.swift in Sources */, 34D3AFF61E37A36A004100F9 /* UICollectionView+Cells.swift in Sources */, @@ -4217,6 +4335,7 @@ 340475811E081B3300C92850 /* iosOGLContextFactory.mm in Sources */, 34AB66561FC5AA330078E451 /* TransportTransitPedestrian.swift in Sources */, 6741A9B11BF340DE002C974C /* MWMAPIBarView.mm in Sources */, + 3467CEB2202C6EEE00D3C670 /* BMCNotificationsHeader.swift in Sources */, 34F4072F1E9E1AFF00E57AC0 /* BannersCache.swift in Sources */, 34D3B0211E389D05004100F9 /* MWMEditorAddAdditionalNameTableViewCell.mm in Sources */, 3486B51E1E27AD590069C126 /* MWMFrameworkHelper.mm in Sources */, @@ -4230,6 +4349,7 @@ 34D3B01B1E389D05004100F9 /* MWMButtonCell.mm in Sources */, 3486B5161E27AD3B0069C126 /* Framework.cpp in Sources */, 34ABA6291C2D567B00FE1BEC /* MWMInputLoginValidator.mm in Sources */, + 3404F49D2028A2430090E401 /* BMCActionsCreateCell.swift in Sources */, F6E2FF091E097BA00083EBEC /* MWMSearchHistoryManager.mm in Sources */, F6E2FD8F1E097BA00083EBEC /* MWMNoMapsViewController.mm in Sources */, F63AF50B1EA6213F00A1DB98 /* FilterRatingCell.swift in Sources */, @@ -4247,6 +4367,7 @@ 6741A9B81BF340DE002C974C /* MapViewController.mm in Sources */, 34AB662C1FC5AA330078E451 /* RouteManagerViewModel.swift in Sources */, 6741A9B91BF340DE002C974C /* MWMRateAlert.mm in Sources */, + 3404F48B202894EA0090E401 /* BMCViewController.swift in Sources */, 349D1ABC1E2D05EF004A2006 /* SearchBar.swift in Sources */, F6E2FD7A1E097BA00083EBEC /* MWMMapDownloaderDefaultDataSource.mm in Sources */, 34E50DF81F6FCC96008EED49 /* UGCReviewCell.swift in Sources */, @@ -4297,6 +4418,7 @@ F6FE3C391CC50FFD00A73196 /* MWMPlaceDoesntExistAlert.mm in Sources */, F6E2FDFE1E097BA00083EBEC /* MWMOpeningHoursClosedSpanTableViewCell.mm in Sources */, F6E2FEDC1E097BA00083EBEC /* MWMSearchManager+Filter.mm in Sources */, + 34B846A12029DCC10081ECCD /* BMCCategoriesHeader.swift in Sources */, 346DB8341E5C4F6700E3123E /* GalleryViewController.swift in Sources */, F61757ED1FC73027000AD0D0 /* DiscoveryOnlineTemplateCell.swift in Sources */, 34943BB71E26222300B14F84 /* WelcomeProtocol.swift in Sources */, @@ -4345,6 +4467,7 @@ F6664C131E645A4100E703C2 /* MWMPPReviewCell.mm in Sources */, F6E2FE431E097BA00083EBEC /* MWMDirectionView.mm in Sources */, 3486B50D1E27A6DA0069C126 /* MWMPushNotifications.mm in Sources */, + 3404F490202898CC0090E401 /* BMCModels.swift in Sources */, F6E2FD561E097BA00083EBEC /* MWMMapDownloaderButtonTableViewCell.mm in Sources */, 3462258F1DDC5DBA001E8752 /* MWMSearchNoResultsAlert.mm in Sources */, 34AB66171FC5AA320078E451 /* MWMiPhoneRoutePreview.mm in Sources */, @@ -4357,6 +4480,7 @@ 6741A9EC1BF340DE002C974C /* MWMCircularProgress.mm in Sources */, 342CC5F21C2D7730005F3FE5 /* MWMAuthorizationLoginViewController.mm in Sources */, 340475591E081A4600C92850 /* WebViewController.mm in Sources */, + 3404F4992028A20D0090E401 /* BMCCategoryCell.swift in Sources */, 34F407411E9E1AFF00E57AC0 /* RBBanner.swift in Sources */, 3444DFD21F17620C00E73099 /* MWMMapWidgetsHelper.mm in Sources */, 348A8DFB1F66775A00D83026 /* RatingViewSettings.swift in Sources */, @@ -4413,6 +4537,7 @@ 3454D7DD1E07F045004AF2AD /* UISwitch+RuntimeAttributes.m in Sources */, 340416581E7C0D4100E2B6D6 /* PhotosOverlayView.swift in Sources */, F6E2FE821E097BA00083EBEC /* MWMPlacePageOpeningHoursDayView.mm in Sources */, + 340FDC092031C39E00F140AD /* BMCPermissionsPendingCell.swift in Sources */, F6E2FD6B1E097BA00083EBEC /* MWMMapDownloaderSubplaceTableViewCell.mm in Sources */, 3409D50B1FC6D8D2000F9B3E /* FilterCheckCell.swift in Sources */, 3472B5CB200F43EF00DC6CD5 /* BackgroundFetchScheduler.swift in Sources */, @@ -4540,6 +4665,7 @@ 34E50DD81F6FCAB1008EED49 /* UGCSummaryRatingCell.swift in Sources */, 6741AA281BF340DE002C974C /* MWMAlert.mm in Sources */, F6E2FF571E097BA00083EBEC /* MWMMobileInternetViewController.mm in Sources */, + 3404F4952028A1B80090E401 /* BMCPermissionsCell.swift in Sources */, 340416441E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift in Sources */, 34AB66261FC5AA330078E451 /* RouteManagerDimView.swift in Sources */, 6741AA291BF340DE002C974C /* ColorPickerView.mm in Sources */, @@ -4548,6 +4674,9 @@ 3444DFDE1F18A5AF00E73099 /* SideButtonsArea.swift in Sources */, 3451F4EE1F026DAF00A981F2 /* PlacePageTaxiCell.swift in Sources */, 34EE25AA1EFA726400F870AB /* ViatorItemModel.swift in Sources */, + 3404F48E2028966C0090E401 /* BMCViewModel.swift in Sources */, + 3467CEB6202C6FA900D3C670 /* BMCNotificationsCell.swift in Sources */, + 34B846A32029DFEB0081ECCD /* BMCPermissionsHeader.swift in Sources */, F6E2FD9E1E097BA00083EBEC /* MWMEditBookmarkController.mm in Sources */, 349FC54B1F680DAE00968C9F /* ExpandableTextViewSettings.swift in Sources */, F6E2FE0A1E097BA00083EBEC /* MWMOpeningHoursDeleteScheduleTableViewCell.mm in Sources */,