diff --git a/Package.swift b/Package.swift index 25ebb908a90ad60198cd069fbab071902387730d..b8f43d824ae319c2f02e73dbdc273420c745b043 100644 --- a/Package.swift +++ b/Package.swift @@ -28,6 +28,7 @@ let package = Package( .library(name: "PushFeature", targets: ["PushFeature"]), .library(name: "SFTPFeature", targets: ["SFTPFeature"]), .library(name: "CrashService", targets: ["CrashService"]), + .library(name: "TermsFeature", targets: ["TermsFeature"]), .library(name: "Presentation", targets: ["Presentation"]), .library(name: "ToastFeature", targets: ["ToastFeature"]), .library(name: "BackupFeature", targets: ["BackupFeature"]), @@ -115,7 +116,7 @@ let package = Package( ), .package( url: "https://git.xx.network/elixxir/client-ios-db.git", - .upToNextMajor(from: "1.0.8") + .upToNextMajor(from: "1.1.0") ), .package( url: "https://github.com/firebase/firebase-ios-sdk.git", @@ -133,6 +134,10 @@ let package = Package( url: "https://github.com/pointfreeco/swift-custom-dump.git", .upToNextMajor(from: "0.5.0") ), + .package( + url: "https://github.com/swiftcsv/SwiftCSV.git", + from: "0.8.0" + ), .package( url: "https://github.com/pointfreeco/xctest-dynamic-overlay.git", .upToNextMajor(from: "0.3.3") @@ -150,6 +155,7 @@ let package = Package( .target(name: "MenuFeature"), .target(name: "PushFeature"), .target(name: "SFTPFeature"), + .target(name: "TermsFeature"), .target(name: "ToastFeature"), .target(name: "CrashService"), .target(name: "BackupFeature"), @@ -505,6 +511,15 @@ let package = Package( .target(name: "DropboxFeature"), .target(name: "VersionChecking"), .target(name: "DependencyInjection"), + .product(name: "SwiftCSV", package: "SwiftCSV"), + ] + ), + .target( + name: "TermsFeature", + dependencies: [ + .target(name: "Theme"), + .target(name: "Shared"), + .target(name: "Defaults") ] ), .target( diff --git a/Sources/App/DependencyRegistrator.swift b/Sources/App/DependencyRegistrator.swift index 05d03f78e33764ef8a3f3cd325b5fa1b9d958c18..e24711457082d4ee7ba1bb51005e73762f656c10 100644 --- a/Sources/App/DependencyRegistrator.swift +++ b/Sources/App/DependencyRegistrator.swift @@ -34,6 +34,7 @@ import DependencyInjection import ScanFeature import ChatFeature import MenuFeature +import TermsFeature import BackupFeature import SearchFeature import LaunchFeature @@ -111,8 +112,16 @@ struct DependencyRegistrator { // MARK: Coordinators + container.register( + TermsCoordinator.live( + usernameFactory: OnboardingUsernameController.init(_:), + chatListFactory: ChatListController.init + ) + ) + container.register( LaunchCoordinator( + termsFactory: TermsConditionsController.init(_:), searchFactory: SearchContainerController.init, requestsFactory: RequestsContainerController.init, chatListFactory: ChatListController.init, @@ -206,6 +215,7 @@ struct DependencyRegistrator { searchFactory: SearchContainerController.init, welcomeFactory: OnboardingWelcomeController.init, chatListFactory: ChatListController.init, + termsFactory: TermsConditionsController.init(_:), usernameFactory: OnboardingUsernameController.init(_:), restoreListFactory: RestoreListController.init(_:), successFactory: OnboardingSuccessController.init(_:), diff --git a/Sources/BackupFeature/Controllers/BackupController.swift b/Sources/BackupFeature/Controllers/BackupController.swift index a9942d8c03466fe8b620d156c9eb1cc02f2ef70a..822ebbc77c3fa46acd0d842579382101aac5b3f9 100644 --- a/Sources/BackupFeature/Controllers/BackupController.swift +++ b/Sources/BackupFeature/Controllers/BackupController.swift @@ -11,6 +11,13 @@ public final class BackupController: UIViewController { private let viewModel = BackupViewModel.live() private var cancellables = Set<AnyCancellable>() + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" + navigationController?.navigationBar + .customize(backgroundColor: Asset.neutralWhite.color) + } + public override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = Asset.neutralWhite.color @@ -21,19 +28,12 @@ public final class BackupController: UIViewController { } private func setupNavigationBar() { - navigationItem.backButtonTitle = "" - let title = UILabel() title.text = Localized.Backup.header title.textColor = Asset.neutralActive.color title.font = Fonts.Mulish.semiBold.font(size: 18.0) - - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - - navigationItem.leftBarButtonItem = UIBarButtonItem( - customView: UIStackView(arrangedSubviews: [back, title]) - ) + navigationItem.leftBarButtonItem = UIBarButtonItem(customView: title) + navigationItem.leftItemsSupplementBackButton = true } private func setupBindings() { @@ -70,8 +70,4 @@ public final class BackupController: UIViewController { } } } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } diff --git a/Sources/ChatFeature/Controllers/GroupChatController.swift b/Sources/ChatFeature/Controllers/GroupChatController.swift index b168e70c438b6660f637758270bc28761dfa5c0c..2411786865669256bff58b618488bf5fbab36aa4 100644 --- a/Sources/ChatFeature/Controllers/GroupChatController.swift +++ b/Sources/ChatFeature/Controllers/GroupChatController.swift @@ -122,14 +122,10 @@ public final class GroupChatController: UIViewController { } private func setupNavigationBar() { - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - let more = UIButton() more.setImage(Asset.chatMore.image, for: .normal) more.addTarget(self, action: #selector(didTapDots), for: .touchUpInside) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: back) navigationItem.titleView = header navigationItem.rightBarButtonItem = UIBarButtonItem(customView: more) } @@ -229,10 +225,6 @@ public final class GroupChatController: UIViewController { .store(in: &cancellables) } - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } - @objc private func didTapDots() { coordinator.toMembersList(members, from: self) } diff --git a/Sources/ChatFeature/Controllers/SingleChatController.swift b/Sources/ChatFeature/Controllers/SingleChatController.swift index f34be438f7df3afd84d5ba7c6db04612bc891cc6..d1d7785af5663f567a8803fce22aa107cbfe0934 100644 --- a/Sources/ChatFeature/Controllers/SingleChatController.swift +++ b/Sources/ChatFeature/Controllers/SingleChatController.swift @@ -35,7 +35,6 @@ public final class SingleChatController: UIViewController { lazy private var avatarView = AvatarView() lazy private var moreButton = UIButton() - lazy private var backButton = UIButton.back() lazy private var screenView = ChatView() lazy private var sheet = SheetController() @@ -168,8 +167,6 @@ public final class SingleChatController: UIViewController { nameLabel.textColor = Asset.neutralActive.color nameLabel.font = Fonts.Mulish.semiBold.font(size: 18.0) - backButton.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - moreButton.setImage(Asset.chatMore.image, for: .normal) moreButton.addTarget(self, action: #selector(didTapDots), for: .touchUpInside) @@ -188,12 +185,8 @@ public final class SingleChatController: UIViewController { $0.right.lessThanOrEqualToSuperview() } - let stackView = UIStackView() - stackView.addArrangedSubview(backButton) - stackView.addArrangedSubview(infoView) - navigationItem.rightBarButtonItem = UIBarButtonItem(customView: moreButton) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: stackView) + navigationItem.leftBarButtonItem = UIBarButtonItem(customView: infoView) } private func setupInputController() { @@ -462,10 +455,6 @@ public final class SingleChatController: UIViewController { @objc private func didTapInfo() { coordinator.toContact(viewModel.contact, from: self) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } extension SingleChatController: UICollectionViewDataSource { diff --git a/Sources/ChatListFeature/Controller/ChatListController.swift b/Sources/ChatListFeature/Controller/ChatListController.swift index bddccd284ce09ecb2b9e479044aa8fb41eb5e757..57e744a3b29d7ab15371624fe2ce0932e7f9d772 100644 --- a/Sources/ChatListFeature/Controller/ChatListController.swift +++ b/Sources/ChatListFeature/Controller/ChatListController.swift @@ -36,6 +36,13 @@ public final class ChatListController: UIViewController { } } + public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + navigationItem.backButtonTitle = "" + } + + required init?(coder: NSCoder) { nil } + public override func loadView() { view = screenView } @@ -55,7 +62,6 @@ public final class ChatListController: UIViewController { } private func setupNavigationBar() { - navigationItem.backButtonTitle = "" navigationItem.leftBarButtonItem = UIBarButtonItem(customView: topLeftView) navigationItem.rightBarButtonItem = UIBarButtonItem(customView: topRightView) diff --git a/Sources/ContactFeature/Controllers/ContactController.swift b/Sources/ContactFeature/Controllers/ContactController.swift index 02859bab60bf861a5615795a016352a4f4509900..c583654e08d1ec4ae54180a8f8dc6079c6aee9c6 100644 --- a/Sources/ContactFeature/Controllers/ContactController.swift +++ b/Sources/ContactFeature/Controllers/ContactController.swift @@ -30,6 +30,7 @@ public final class ContactController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" statusBarController.style.send(.lightContent) navigationController?.navigationBar .customize(backgroundColor: Asset.neutralBody.color) @@ -43,7 +44,6 @@ public final class ContactController: UIViewController { public override func viewDidLoad() { super.viewDidLoad() - setupNavigationBar() setupScrollView() setupBindings() @@ -62,14 +62,6 @@ public final class ContactController: UIViewController { screenView.set(status: viewModel.contact.authStatus) } - private func setupNavigationBar() { - navigationItem.backButtonTitle = "" - - let back = UIButton.back(color: Asset.neutralWhite.color) - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: back) - } - private func setupScrollView() { addChild(scrollViewController) view.addSubview(scrollViewController.view) @@ -333,10 +325,6 @@ public final class ContactController: UIViewController { coordinator.toDrawer(drawer, from: self) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } extension ContactController: UIImagePickerControllerDelegate { diff --git a/Sources/ContactListFeature/Controllers/CreateGroupController.swift b/Sources/ContactListFeature/Controllers/CreateGroupController.swift index a9559f5c3c38ccdf7a90543a9963748155e6fa6b..e55860fc0a48220413dc33373c915e754351ebe5 100644 --- a/Sources/ContactListFeature/Controllers/CreateGroupController.swift +++ b/Sources/ContactListFeature/Controllers/CreateGroupController.swift @@ -40,22 +40,25 @@ public final class CreateGroupController: UIViewController { view = screenView } + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" + navigationController?.navigationBar + .customize(backgroundColor: Asset.neutralWhite.color) + } + public override func viewDidLoad() { super.viewDidLoad() setupNavigationBar() setupTableAndCollection() setupBindings() + + count = 0 } private func setupNavigationBar() { - navigationItem.backButtonTitle = " " - - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - - navigationItem.leftBarButtonItem = UIBarButtonItem( - customView: UIStackView(arrangedSubviews: [back, titleLabel]) - ) + navigationItem.leftBarButtonItem = UIBarButtonItem(customView: titleLabel) + navigationItem.leftItemsSupplementBackButton = true createButton.setTitle(Localized.CreateGroup.create, for: .normal) createButton.setTitleColor(Asset.brandPrimary.color, for: .normal) @@ -166,10 +169,6 @@ public final class CreateGroupController: UIViewController { ) }.store(in: &cancellables) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } extension CreateGroupController: UITableViewDelegate { diff --git a/Sources/Countries/CountryListController.swift b/Sources/Countries/CountryListController.swift index ab7ca357c0d9f074d73c38c1616cbba81d9ed9c1..a11c0e5724696bc172f58cfdc7b4333288639b7f 100644 --- a/Sources/Countries/CountryListController.swift +++ b/Sources/Countries/CountryListController.swift @@ -24,6 +24,7 @@ public final class CountryListController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" statusBarController.style.send(.darkContent) navigationController?.navigationBar.customize( @@ -46,19 +47,13 @@ public final class CountryListController: UIViewController { } private func setupNavigationBar() { - navigationItem.backButtonTitle = " " - let title = UILabel() title.text = Localized.Countries.title title.textColor = Asset.neutralActive.color title.font = Fonts.Mulish.semiBold.font(size: 18.0) - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - - navigationItem.leftBarButtonItem = UIBarButtonItem( - customView: UIStackView(arrangedSubviews: [back, title]) - ) + navigationItem.leftBarButtonItem = UIBarButtonItem(customView: title) + navigationItem.leftItemsSupplementBackButton = true } private func setupBindings() { @@ -86,10 +81,6 @@ public final class CountryListController: UIViewController { screenView.tableView.delegate = self screenView.tableView.dataSource = dataSource } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let country = dataSource.itemIdentifier(for: indexPath) { diff --git a/Sources/Defaults/KeyObject.swift b/Sources/Defaults/KeyObject.swift index 7757f7ed4fa550c478736b76cd7c1036ef4fde34..0ade4e83639f54a5181b4292936a2d9dee049f60 100644 --- a/Sources/Defaults/KeyObject.swift +++ b/Sources/Defaults/KeyObject.swift @@ -21,6 +21,7 @@ public enum Key: String { // MARK: General case theme + case acceptedTerms // MARK: Requests diff --git a/Sources/LaunchFeature/LaunchController.swift b/Sources/LaunchFeature/LaunchController.swift index 9d904232f4f1900e8ea1880a83717a4b9eadafd6..cf8b093b2131d2becbb7d5e6bb55a3b02fb73c0e 100644 --- a/Sources/LaunchFeature/LaunchController.swift +++ b/Sources/LaunchFeature/LaunchController.swift @@ -2,6 +2,7 @@ import HUD import UIKit import Shared import Combine +import Defaults import PushFeature import DependencyInjection @@ -9,6 +10,8 @@ public final class LaunchController: UIViewController { @Dependency private var hud: HUD @Dependency private var coordinator: LaunchCoordinating + @KeyObject(.acceptedTerms, defaultValue: false) var didAcceptTerms: Bool + lazy private var screenView = LaunchView() private let blocker = UpdateBlocker() @@ -50,6 +53,11 @@ public final class LaunchController: UIViewController { .sink { [unowned self] in switch $0 { case .chats: + guard didAcceptTerms == true else { + coordinator.toTerms(from: self) + return + } + if let pushRoute = pendingPushRoute { switch pushRoute { case .requests: diff --git a/Sources/LaunchFeature/LaunchCoordinator.swift b/Sources/LaunchFeature/LaunchCoordinator.swift index 28ac7f90d6833c85d43cf5dfc2fd56a0bc4dad23..37035773d6cfd875d5acf6de309b4ac84d72ae6f 100644 --- a/Sources/LaunchFeature/LaunchCoordinator.swift +++ b/Sources/LaunchFeature/LaunchCoordinator.swift @@ -5,6 +5,7 @@ import Presentation public protocol LaunchCoordinating { func toChats(from: UIViewController) + func toTerms(from: UIViewController) func toRequests(from: UIViewController) func toSearch(searching: String, from: UIViewController) func toOnboarding(with: String, from: UIViewController) @@ -15,6 +16,7 @@ public protocol LaunchCoordinating { public struct LaunchCoordinator: LaunchCoordinating { var replacePresenter: Presenting = ReplacePresenter() + var termsFactory: (String?) -> UIViewController var searchFactory: (String) -> UIViewController var requestsFactory: () -> UIViewController var chatListFactory: () -> UIViewController @@ -23,6 +25,7 @@ public struct LaunchCoordinator: LaunchCoordinating { var groupChatFactory: (GroupInfo) -> UIViewController public init( + termsFactory: @escaping (String?) -> UIViewController, searchFactory: @escaping (String) -> UIViewController, requestsFactory: @escaping () -> UIViewController, chatListFactory: @escaping () -> UIViewController, @@ -30,6 +33,7 @@ public struct LaunchCoordinator: LaunchCoordinating { singleChatFactory: @escaping (Contact) -> UIViewController, groupChatFactory: @escaping (GroupInfo) -> UIViewController ) { + self.termsFactory = termsFactory self.searchFactory = searchFactory self.requestsFactory = requestsFactory self.chatListFactory = chatListFactory @@ -46,6 +50,11 @@ public extension LaunchCoordinator { replacePresenter.present(chatListScreen, screen, from: parent) } + func toTerms(from parent: UIViewController) { + let screen = termsFactory(nil) + replacePresenter.present(screen, from: parent) + } + func toChats(from parent: UIViewController) { let screen = chatListFactory() replacePresenter.present(screen, from: parent) diff --git a/Sources/LaunchFeature/LaunchViewModel.swift b/Sources/LaunchFeature/LaunchViewModel.swift index 0b8354dacde4b0a364b2f7d2b4a3b7701a66093e..68f3fab87eff23ccdee9985c4bec27f3101f09df 100644 --- a/Sources/LaunchFeature/LaunchViewModel.swift +++ b/Sources/LaunchFeature/LaunchViewModel.swift @@ -1,6 +1,7 @@ import HUD import Shared import Models +import SwiftCSV import Combine import Defaults @@ -46,6 +47,10 @@ final class LaunchViewModel { routeSubject.eraseToAnyPublisher() } + var mainScheduler: AnySchedulerOf<DispatchQueue> = { + DispatchQueue.main.eraseToAnyScheduler() + }() + var backgroundScheduler: AnySchedulerOf<DispatchQueue> = { DispatchQueue.global().eraseToAnyScheduler() }() @@ -57,67 +62,75 @@ final class LaunchViewModel { private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none) func viewDidAppear() { - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in - self?.hudSubject.send(.on) - self?.checkVersion() - } - } - - private func checkVersion() { - versionChecker().sink { [unowned self] in - switch $0 { - case .upToDate: - versionApproved() - case .failure(let error): - versionFailed(error: error) - case .updateRequired(let info): - versionUpdateRequired(info) - case .updateRecommended(let info): - versionUpdateRecommended(info) - } - }.store(in: &cancellables) - } - - func versionApproved() { - network.writeLogs() - - network.updateNDF { [weak self] in + mainScheduler.schedule(after: .init(.now() + 1)) { [weak self] in guard let self = self else { return } - switch $0 { - case .success(let ndf): - self.network.updateErrors() + self.hudSubject.send(.on) - guard self.network.hasClient else { - self.hudSubject.send(.none) - self.routeSubject.send(.onboarding(ndf)) - self.dropboxService.unlink() - try? self.keychainHandler.clear() - return + self.versionChecker().sink { [unowned self] in + switch $0 { + case .upToDate: + self.versionApproved() + case .failure(let error): + self.versionFailed(error: error) + case .updateRequired(let info): + self.versionUpdateRequired(info) + case .updateRecommended(let info): + self.versionUpdateRecommended(info) } + }.store(in: &self.cancellables) + } + } - guard self.username != nil else { - self.network.purgeFiles() - self.hudSubject.send(.none) - self.routeSubject.send(.onboarding(ndf)) - self.dropboxService.unlink() - try? self.keychainHandler.clear() - return - } + func versionApproved() { + Task { + do { + network.writeLogs() + + // TODO: Retry inifitely if fails + let _ = try await fetchBannedList() - self.backgroundScheduler.schedule { [weak self] in + network.updateNDF { [weak self] in guard let self = self else { return } - do { - let session = try self.getSession(ndf) - DependencyInjection.Container.shared.register(session as SessionType) - self.hudSubject.send(.none) - self.checkBiometrics() - } catch { + switch $0 { + case .success(let ndf): + self.network.updateErrors() + + guard self.network.hasClient else { + self.hudSubject.send(.none) + self.routeSubject.send(.onboarding(ndf)) + self.dropboxService.unlink() + try? self.keychainHandler.clear() + return + } + + guard self.username != nil else { + self.network.purgeFiles() + self.hudSubject.send(.none) + self.routeSubject.send(.onboarding(ndf)) + self.dropboxService.unlink() + try? self.keychainHandler.clear() + return + } + + self.backgroundScheduler.schedule { [weak self] in + guard let self = self else { return } + + do { + let session = try self.getSession(ndf) + DependencyInjection.Container.shared.register(session as SessionType) + self.hudSubject.send(.none) + self.checkBiometrics() + } catch { + self.hudSubject.send(.error(HUDError(with: error))) + } + } + case .failure(let error): self.hudSubject.send(.error(HUDError(with: error))) } } - case .failure(let error): + } catch { self.hudSubject.send(.error(HUDError(with: error))) } } @@ -195,4 +208,18 @@ final class LaunchViewModel { self.routeSubject.send(.chats) } } + + private func fetchBannedList() async throws -> Data { + let url = URL(string: "https://elixxir-bins.s3.us-west-1.amazonaws.com/client/bannedUsers/banned.csv") + return try await withCheckedThrowingContinuation { continuation in + URLSession.shared.dataTask(with: url!) { data, _, error in + if let error = error { + return continuation.resume(throwing: error) + } + + guard let data = data else { fatalError("?") } + return continuation.resume(returning: data) + }.resume() + } + } } diff --git a/Sources/OnboardingFeature/Controllers/OnboardingEmailConfirmationController.swift b/Sources/OnboardingFeature/Controllers/OnboardingEmailConfirmationController.swift index 90c210569b58b904473f5c2e2020740e2945a09d..578a27dc5bdedfcd58ab9dbecfd1338c07bad324 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingEmailConfirmationController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingEmailConfirmationController.swift @@ -34,15 +34,13 @@ public final class OnboardingEmailConfirmationController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" statusBarController.style.send(.darkContent) navigationController?.navigationBar.customize(translucent: true) } public override func viewDidLoad() { super.viewDidLoad() - navigationItem.backButtonTitle = " " - - setupNavigationBar() setupScrollView() setupBindings() @@ -58,12 +56,6 @@ public final class OnboardingEmailConfirmationController: UIViewController { } } - private func setupNavigationBar() { - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: back) - } - private func setupScrollView() { addChild(scrollViewController) view.addSubview(scrollViewController.view) @@ -157,8 +149,4 @@ public final class OnboardingEmailConfirmationController: UIViewController { coordinator.toDrawer(drawer, from: self) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } diff --git a/Sources/OnboardingFeature/Controllers/OnboardingPhoneConfirmationController.swift b/Sources/OnboardingFeature/Controllers/OnboardingPhoneConfirmationController.swift index 0017798bc5fc2a4703a56cf6b4fc2a45fbcffa55..6207a5a7c9b19b1a5184557eb45cd07bd8aeb21b 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingPhoneConfirmationController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingPhoneConfirmationController.swift @@ -34,15 +34,13 @@ public final class OnboardingPhoneConfirmationController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" statusBarController.style.send(.darkContent) navigationController?.navigationBar.customize(translucent: true) } public override func viewDidLoad() { super.viewDidLoad() - navigationItem.backButtonTitle = " " - - setupNavigationBar() setupScrollView() setupBindings() @@ -58,12 +56,6 @@ public final class OnboardingPhoneConfirmationController: UIViewController { } } - private func setupNavigationBar() { - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: back) - } - private func setupScrollView() { addChild(scrollViewController) view.addSubview(scrollViewController.view) @@ -157,8 +149,4 @@ public final class OnboardingPhoneConfirmationController: UIViewController { coordinator.toDrawer(drawer, from: self) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } diff --git a/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift b/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift index d95169bbd9c5ce078687a77c96768d91e9d2336d..d9b970329040862abe2222493c0ecaf0ee2b34c4 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift @@ -27,6 +27,7 @@ public final class OnboardingStartController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" navigationController?.navigationBar.customize(translucent: true) } @@ -52,7 +53,7 @@ public final class OnboardingStartController: UIViewController { super.viewDidLoad() screenView.startButton.publisher(for: .touchUpInside) - .sink { [unowned self] in coordinator.toUsername(with: ndf, from: self) } + .sink { [unowned self] in coordinator.toTerms(ndf: ndf, from: self) } .store(in: &cancellables) } } diff --git a/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift b/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift index 3bc4493ebd99899a4b9ea646964816ff0140009e..d42e1f81f055d800cb2fe86afb5ac0870c6ca39a 100644 --- a/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift +++ b/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift @@ -22,6 +22,7 @@ public final class OnboardingUsernameController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" statusBarController.style.send(.darkContent) navigationController?.navigationBar.customize(translucent: true) } diff --git a/Sources/OnboardingFeature/Coordinator/OnboardingCoordinator.swift b/Sources/OnboardingFeature/Coordinator/OnboardingCoordinator.swift index 59bf66c4e95c27f4a151472e473f657c88c13e1e..9ea57da2c51a02679f40c9132b88498fb139fa68 100644 --- a/Sources/OnboardingFeature/Coordinator/OnboardingCoordinator.swift +++ b/Sources/OnboardingFeature/Coordinator/OnboardingCoordinator.swift @@ -11,6 +11,7 @@ public protocol OnboardingCoordinating { func toEmail(from: UIViewController) func toPhone(from: UIViewController) func toWelcome(from: UIViewController) + func toTerms(ndf: String, from: UIViewController) func toUsername(with: String, from: UIViewController) func toRestoreList(with: String, from: UIViewController) func toDrawer(_: UIViewController, from: UIViewController) @@ -46,6 +47,7 @@ public struct OnboardingCoordinator: OnboardingCoordinating { var chatListFactory: () -> UIViewController var usernameFactory: (String) -> UIViewController var restoreListFactory: (String) -> UIViewController + var termsFactory: (String?) -> UIViewController var successFactory: (OnboardingSuccessModel) -> UIViewController var countriesFactory: (@escaping (Country) -> Void) -> UIViewController var phoneConfirmationFactory: (AttributeConfirmation, @escaping AttributeControllerClosure) -> UIViewController @@ -57,6 +59,7 @@ public struct OnboardingCoordinator: OnboardingCoordinating { searchFactory: @escaping (String?) -> UIViewController, welcomeFactory: @escaping () -> UIViewController, chatListFactory: @escaping () -> UIViewController, + termsFactory: @escaping (String?) -> UIViewController, usernameFactory: @escaping (String) -> UIViewController, restoreListFactory: @escaping (String) -> UIViewController, successFactory: @escaping (OnboardingSuccessModel) -> UIViewController, @@ -65,6 +68,7 @@ public struct OnboardingCoordinator: OnboardingCoordinating { emailConfirmationFactory: @escaping (AttributeConfirmation, @escaping AttributeControllerClosure) -> UIViewController ) { self.emailFactory = emailFactory + self.termsFactory = termsFactory self.phoneFactory = phoneFactory self.searchFactory = searchFactory self.welcomeFactory = welcomeFactory @@ -79,6 +83,14 @@ public struct OnboardingCoordinator: OnboardingCoordinating { } public extension OnboardingCoordinator { + func toTerms( + ndf: String, + from parent: UIViewController + ) { + let screen = termsFactory(ndf) + pushPresenter.present(screen, from: parent) + } + func toEmail(from parent: UIViewController) { let screen = emailFactory() replacePresenter.present(screen, from: parent) diff --git a/Sources/Permissions/RequestPermissionController.swift b/Sources/Permissions/RequestPermissionController.swift index 8c787bed838c4e621c9fb17a0e713214dbf18946..d892ab60e8722633e8d7c2930eb7ec97b4049d8c 100644 --- a/Sources/Permissions/RequestPermissionController.swift +++ b/Sources/Permissions/RequestPermissionController.swift @@ -25,13 +25,13 @@ public final class RequestPermissionController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" statusBarController.style.send(.darkContent) navigationController?.navigationBar.customize(backgroundColor: Asset.neutralWhite.color) } public override func viewDidLoad() { super.viewDidLoad() - setupNavigationBar() setupBindings() } @@ -60,14 +60,6 @@ public final class RequestPermissionController: UIViewController { } } - private func setupNavigationBar() { - navigationItem.backButtonTitle = "" - - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: back) - } - private func setupBindings() { screenView.notNowButton .publisher(for: .touchUpInside) @@ -105,7 +97,4 @@ public final class RequestPermissionController: UIViewController { }.store(in: &cancellables) } - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } diff --git a/Sources/ProfileFeature/Controllers/ProfileCodeController.swift b/Sources/ProfileFeature/Controllers/ProfileCodeController.swift index c005b0bf9c10daa6db1f8521799da86bbfad26a4..d612b9e9e79c4dbb362b5a0c7782eb1463dc4fe6 100644 --- a/Sources/ProfileFeature/Controllers/ProfileCodeController.swift +++ b/Sources/ProfileFeature/Controllers/ProfileCodeController.swift @@ -22,6 +22,7 @@ public final class ProfileCodeController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" navigationController?.navigationBar .customize(backgroundColor: Asset.neutralWhite.color) } @@ -39,20 +40,11 @@ public final class ProfileCodeController: UIViewController { public override func viewDidLoad() { super.viewDidLoad() - setupNavigationBar() setupScrollView() setupBindings() setupDetail() } - private func setupNavigationBar() { - navigationItem.backButtonTitle = " " - - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: back) - } - private func setupScrollView() { addChild(scrollViewController) view.addSubview(scrollViewController.view) @@ -128,8 +120,4 @@ public final class ProfileCodeController: UIViewController { screenView.set(content, isEmail: confirmation.isEmail) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } diff --git a/Sources/ProfileFeature/Controllers/ProfileEmailController.swift b/Sources/ProfileFeature/Controllers/ProfileEmailController.swift index 97ced65c65a10b7d4ac8033a5b2c0d80cf959997..3fb88d2b649ed6f9f20b8189467a021563188c13 100644 --- a/Sources/ProfileFeature/Controllers/ProfileEmailController.swift +++ b/Sources/ProfileFeature/Controllers/ProfileEmailController.swift @@ -19,6 +19,7 @@ public final class ProfileEmailController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" statusBarController.style.send(.darkContent) navigationController?.navigationBar .customize(backgroundColor: Asset.neutralWhite.color) @@ -26,19 +27,10 @@ public final class ProfileEmailController: UIViewController { public override func viewDidLoad() { super.viewDidLoad() - setupNavigationBar() setupScrollView() setupBindings() } - private func setupNavigationBar() { - navigationItem.backButtonTitle = " " - - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: back) - } - private func setupScrollView() { addChild(scrollViewController) view.addSubview(scrollViewController.view) @@ -89,8 +81,4 @@ public final class ProfileEmailController: UIViewController { .sink { [unowned self] in viewModel.didTapNext() } .store(in: &cancellables) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } diff --git a/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift b/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift index eb77fd14d36bce3960f30a73aa4ac7c117e453e7..01737802adc9611476892ca38675b5dd8ff6d743 100644 --- a/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift +++ b/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift @@ -21,26 +21,18 @@ public final class ProfilePhoneController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" statusBarController.style.send(.darkContent) navigationController?.navigationBar - .customize(backgroundColor: Asset.neutralWhite.color) + .customize(backgroundColor: Asset.neutralWhite.color) } public override func viewDidLoad() { super.viewDidLoad() - setupNavigationBar() setupScrollView() setupBindings() } - private func setupNavigationBar() { - navigationItem.backButtonTitle = " " - - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: back) - } - private func setupScrollView() { addChild(scrollViewController) view.addSubview(scrollViewController.view) @@ -107,8 +99,4 @@ public final class ProfilePhoneController: UIViewController { .sink { [unowned self] in viewModel.didTapNext() } .store(in: &cancellables) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } diff --git a/Sources/RestoreFeature/Controllers/RestoreController.swift b/Sources/RestoreFeature/Controllers/RestoreController.swift index b88932c148df5249e2c88184b3506e335596020f..f2081aaef8eca0773f552bf685976dbfe67d75c9 100644 --- a/Sources/RestoreFeature/Controllers/RestoreController.swift +++ b/Sources/RestoreFeature/Controllers/RestoreController.swift @@ -26,6 +26,12 @@ public final class RestoreController: UIViewController { presentWarning() } + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" + navigationController?.navigationBar.customize() + } + public override func viewDidLoad() { super.viewDidLoad() setupNavigationBar() @@ -33,19 +39,13 @@ public final class RestoreController: UIViewController { } private func setupNavigationBar() { - navigationItem.backButtonTitle = "" - let title = UILabel() title.text = Localized.AccountRestore.header title.textColor = Asset.neutralActive.color title.font = Fonts.Mulish.semiBold.font(size: 18.0) - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - - navigationItem.leftBarButtonItem = UIBarButtonItem( - customView: UIStackView(arrangedSubviews: [back, title]) - ) + navigationItem.leftBarButtonItem = UIBarButtonItem(customView: title) + navigationItem.leftItemsSupplementBackButton = true } private func setupBindings() { diff --git a/Sources/RestoreFeature/Controllers/RestoreListController.swift b/Sources/RestoreFeature/Controllers/RestoreListController.swift index b396d1069df7008772290e5c9e2f275adb5ccf4b..33470e44212a67e1ee6833cfd70664aca840321f 100644 --- a/Sources/RestoreFeature/Controllers/RestoreListController.swift +++ b/Sources/RestoreFeature/Controllers/RestoreListController.swift @@ -30,27 +30,13 @@ public final class RestoreListController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" navigationController?.navigationBar.customize(translucent: true) } public override func viewDidLoad() { super.viewDidLoad() - setupNavigationBar() - setupBindings() - } - - private func setupNavigationBar() { - navigationItem.backButtonTitle = "" - - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - - navigationItem.leftBarButtonItem = UIBarButtonItem( - customView: UIStackView(arrangedSubviews: [back]) - ) - } - private func setupBindings() { viewModel.hudPublisher .receive(on: DispatchQueue.main) .sink { [hud] in hud.update(with: $0) } diff --git a/Sources/SFTPFeature/SFTPController.swift b/Sources/SFTPFeature/SFTPController.swift index f80908b9cdf6106bacef3c2fa9f6c4eac46e8145..21bf8ca1f224ad3b5439d619fbd27025a2a96295 100644 --- a/Sources/SFTPFeature/SFTPController.swift +++ b/Sources/SFTPFeature/SFTPController.swift @@ -21,10 +21,15 @@ public final class SFTPController: UIViewController { required init?(coder: NSCoder) { nil } + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" + navigationController?.navigationBar.customize(translucent: true) + } + public override func viewDidLoad() { super.viewDidLoad() setupScrollView() - setupNavigationBar() setupBindings() } @@ -38,17 +43,6 @@ public final class SFTPController: UIViewController { scrollViewController.contentView = screenView } - private func setupNavigationBar() { - navigationItem.backButtonTitle = "" - - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - - navigationItem.leftBarButtonItem = UIBarButtonItem( - customView: UIStackView(arrangedSubviews: [back]) - ) - } - private func setupBindings() { viewModel.hudPublisher .receive(on: DispatchQueue.main) @@ -89,8 +83,4 @@ public final class SFTPController: UIViewController { .sink { [unowned self] in viewModel.didTapLogin() } .store(in: &cancellables) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } diff --git a/Sources/SearchFeature/Controllers/SearchContainerController.swift b/Sources/SearchFeature/Controllers/SearchContainerController.swift index 9cba53034d3a3a09a5fc5bcd80e4c48b7566eb06..3a1bf1e45af9077b7bc70315f5584be93b116242 100644 --- a/Sources/SearchFeature/Controllers/SearchContainerController.swift +++ b/Sources/SearchFeature/Controllers/SearchContainerController.swift @@ -33,6 +33,7 @@ public final class SearchContainerController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" statusBarController.style.send(.darkContent) navigationController?.navigationBar.customize( backgroundColor: Asset.neutralWhite.color @@ -61,19 +62,13 @@ public final class SearchContainerController: UIViewController { } private func setupNavigationBar() { - navigationItem.backButtonTitle = " " + let title = UILabel() + title.text = Localized.Ud.title + title.textColor = Asset.neutralActive.color + title.font = Fonts.Mulish.semiBold.font(size: 18.0) - let titleLabel = UILabel() - titleLabel.text = Localized.Ud.title - titleLabel.textColor = Asset.neutralActive.color - titleLabel.font = Fonts.Mulish.semiBold.font(size: 18.0) - - let backButton = UIButton.back() - backButton.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - - navigationItem.leftBarButtonItem = UIBarButtonItem( - customView: UIStackView(arrangedSubviews: [backButton, titleLabel]) - ) + navigationItem.leftBarButtonItem = UIBarButtonItem(customView: title) + navigationItem.leftItemsSupplementBackButton = true } private func setupBindings() { @@ -98,10 +93,6 @@ public final class SearchContainerController: UIViewController { .store(in: &cancellables) } - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } - private func embedControllers() { addChild(leftController) addChild(rightController) diff --git a/Sources/SettingsFeature/Controllers/AccountDeleteController.swift b/Sources/SettingsFeature/Controllers/AccountDeleteController.swift index 1dc36e2ef958cc0c7fba6177e621915d96cfc4cc..ee5be65a72df17338ab3daca3ddcf58531057338 100644 --- a/Sources/SettingsFeature/Controllers/AccountDeleteController.swift +++ b/Sources/SettingsFeature/Controllers/AccountDeleteController.swift @@ -28,7 +28,6 @@ public final class AccountDeleteController: UIViewController { public override func viewDidLoad() { super.viewDidLoad() - setupNavigationBar() setupScrollView() setupBindings() @@ -42,12 +41,6 @@ public final class AccountDeleteController: UIViewController { } } - private func setupNavigationBar() { - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - navigationItem.leftBarButtonItem = UIBarButtonItem(customView: back) - } - private func setupScrollView() { addChild(scrollViewController) view.addSubview(scrollViewController.view) @@ -84,10 +77,6 @@ public final class AccountDeleteController: UIViewController { .store(in: &cancellables) } - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } - private func presentInfo(title: String, subtitle: String) { let actionButton = CapsuleButton() actionButton.set( diff --git a/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift b/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift index aeb6791eefb9685595edd21c816026de9f2ebb0a..a7d92763d3116ec42ea3fd5c65f8282e69f45471 100644 --- a/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift +++ b/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift @@ -17,6 +17,7 @@ public final class SettingsAdvancedController: UIViewController { public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" navigationController?.navigationBar .customize(backgroundColor: Asset.neutralWhite.color) } @@ -30,19 +31,13 @@ public final class SettingsAdvancedController: UIViewController { } private func setupNavigationBar() { - navigationItem.backButtonTitle = "" - let title = UILabel() title.text = Localized.Settings.Advanced.title title.textColor = Asset.neutralActive.color title.font = Fonts.Mulish.semiBold.font(size: 18.0) - let back = UIButton.back() - back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) - - navigationItem.leftBarButtonItem = UIBarButtonItem( - customView: UIStackView(arrangedSubviews: [back, title]) - ) + navigationItem.leftBarButtonItem = UIBarButtonItem(customView: title) + navigationItem.leftItemsSupplementBackButton = true } private func setupBindings() { @@ -79,8 +74,4 @@ public final class SettingsAdvancedController: UIViewController { screenView.showUsernamesSwitcher.switcherView.setOn(state.isShowingUsernames, animated: true) }.store(in: &cancellables) } - - @objc private func didTapBack() { - navigationController?.popViewController(animated: true) - } } diff --git a/Sources/Shared/AutoGenerated/Strings.swift b/Sources/Shared/AutoGenerated/Strings.swift index 1f65eac1dc1fc32bef4a548cfee612b192f07f3f..03ada00df323371aae8d22c05b50903f9da768d6 100644 --- a/Sources/Shared/AutoGenerated/Strings.swift +++ b/Sources/Shared/AutoGenerated/Strings.swift @@ -1224,6 +1224,17 @@ public enum Localized { } } + public enum Terms { + /// Accept and proceed + public static let accept = Localized.tr("Localizable", "terms.accept") + /// By enabling the checkbox on the left, you agree with the terms and conditions. + public static let radio = Localized.tr("Localizable", "terms.radio") + /// Show terms and conditions + public static let show = Localized.tr("Localizable", "terms.show") + /// Terms #&# Conditions + public static let title = Localized.tr("Localizable", "terms.title") + } + public enum Ud { /// There are no users with that %@. public static func noneFound(_ p1: Any) -> String { diff --git a/Sources/Shared/Extensions/NavigationBar.swift b/Sources/Shared/Extensions/NavigationBar.swift index 2b4d8d254ed4554cbeb8d58dc94df1c0f0f48754..b7efcb698321a974752e5c07cab1542ca0d26d80 100644 --- a/Sources/Shared/Extensions/NavigationBar.swift +++ b/Sources/Shared/Extensions/NavigationBar.swift @@ -4,14 +4,18 @@ public extension UINavigationBar { func customize( translucent: Bool = false, backgroundColor: UIColor = .clear, - shadowColor: UIColor? = nil + shadowColor: UIColor? = nil, + tint: UIColor = Asset.neutralActive.color ) { isTranslucent = translucent let barAppearance = UINavigationBarAppearance() barAppearance.backgroundColor = backgroundColor barAppearance.backgroundEffect = .none barAppearance.shadowColor = shadowColor + + tintColor = tint + compactAppearance = barAppearance standardAppearance = barAppearance - scrollEdgeAppearance = standardAppearance + scrollEdgeAppearance = barAppearance } } diff --git a/Sources/Shared/Resources/en.lproj/Localizable.strings b/Sources/Shared/Resources/en.lproj/Localizable.strings index 4172e91f1c19da1b3d304f289c43c7dd7cd55e63..033e7a6f2ff8bd973d4eaf2cec0755295c8936b5 100644 --- a/Sources/Shared/Resources/en.lproj/Localizable.strings +++ b/Sources/Shared/Resources/en.lproj/Localizable.strings @@ -646,6 +646,17 @@ "backup.SFTP" = "SFTP"; +// Terms & Conditions + +"terms.title" += "Terms #&# Conditions"; +"terms.radio" += "By enabling the checkbox on the left, you agree with the terms and conditions."; +"terms.accept" += "Accept and proceed"; +"terms.show" += "Show terms and conditions"; + // Settings - Delete Account "settings.delete.title" diff --git a/Sources/TermsFeature/RadioButton.swift b/Sources/TermsFeature/RadioButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..43b873ac9939ac8297e466ef4494cf9c78d9512a --- /dev/null +++ b/Sources/TermsFeature/RadioButton.swift @@ -0,0 +1,53 @@ +import UIKit +import Shared + +final class RadioButton: UIControl { + private let filledView = UIView() + private let containerView = UIView() + + init() { + super.init(frame: .zero) + + containerView.layer.borderWidth = 1 + containerView.layer.cornerRadius = 15 + containerView.layer.masksToBounds = true + containerView.layer.borderColor = UIColor.gray.cgColor + + filledView.isHidden = true + filledView.layer.cornerRadius = 10 + filledView.layer.masksToBounds = true + filledView.backgroundColor = Asset.brandPrimary.color + + containerView.isUserInteractionEnabled = false + filledView.isUserInteractionEnabled = false + + addSubview(containerView) + containerView.addSubview(filledView) + + setupConstraints() + } + + required init?(coder: NSCoder) { nil } + + func set(enabled: Bool) { + filledView.isHidden = !enabled + } + + private func setupConstraints() { + containerView.snp.makeConstraints { + $0.width.equalTo(30) + $0.height.equalTo(30) + $0.top.equalToSuperview().offset(5) + $0.left.equalToSuperview().offset(5) + $0.right.equalToSuperview().offset(-5) + $0.bottom.equalToSuperview().offset(-5) + } + + filledView.snp.makeConstraints { + $0.top.equalToSuperview().offset(5) + $0.left.equalToSuperview().offset(5) + $0.right.equalToSuperview().offset(-5) + $0.bottom.equalToSuperview().offset(-5) + } + } +} diff --git a/Sources/TermsFeature/RadioTextComponent.swift b/Sources/TermsFeature/RadioTextComponent.swift new file mode 100644 index 0000000000000000000000000000000000000000..64c7b1cfb3d80ccc85284f7544e286666faeb639 --- /dev/null +++ b/Sources/TermsFeature/RadioTextComponent.swift @@ -0,0 +1,40 @@ +import UIKit +import Shared + +final class RadioTextComponent: UIView { + let titleLabel = UILabel() + let radioButton = RadioButton() + + var isEnabled: Bool = false { + didSet { radioButton.set(enabled: isEnabled) } + } + + init() { + super.init(frame: .zero) + + titleLabel.numberOfLines = 0 + titleLabel.textColor = Asset.neutralBody.color + titleLabel.font = Fonts.Mulish.regular.font(size: 13.0) + + addSubview(titleLabel) + addSubview(radioButton) + + setupConstraints() + } + + required init?(coder: NSCoder) { nil } + + private func setupConstraints() { + titleLabel.snp.makeConstraints { + $0.left.equalTo(radioButton.snp.right).offset(7) + $0.centerY.equalTo(radioButton) + $0.right.equalToSuperview() + } + + radioButton.snp.makeConstraints { + $0.left.equalToSuperview() + $0.top.greaterThanOrEqualToSuperview() + $0.bottom.equalToSuperview() + } + } +} diff --git a/Sources/TermsFeature/TermsConditionsController.swift b/Sources/TermsFeature/TermsConditionsController.swift new file mode 100644 index 0000000000000000000000000000000000000000..27ad6cb78e28b5eca4ee7de044afa317229e941f --- /dev/null +++ b/Sources/TermsFeature/TermsConditionsController.swift @@ -0,0 +1,66 @@ +import UIKit +import Theme +import Shared +import Combine +import Defaults +import DependencyInjection + +public final class TermsConditionsController: UIViewController { + @Dependency var coordinator: TermsCoordinator + @Dependency var statusBarController: StatusBarStyleControlling + + @KeyObject(.acceptedTerms, defaultValue: false) var didAcceptTerms: Bool + + lazy private var screenView = TermsConditionsView() + + private let ndf: String? + private var cancellables = Set<AnyCancellable>() + + public init(_ ndf: String?) { + self.ndf = ndf + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { nil } + + public override func loadView() { + view = screenView + } + + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationItem.backButtonTitle = "" + statusBarController.style.send(.darkContent) + navigationController?.navigationBar.customize(translucent: true) + } + + public override func viewDidLoad() { + super.viewDidLoad() + + screenView.radioComponent + .radioButton + .publisher(for: .touchUpInside) + .sink { [unowned self] in + screenView.radioComponent.isEnabled.toggle() + screenView.nextButton.isEnabled = screenView.radioComponent.isEnabled + }.store(in: &cancellables) + + screenView.nextButton + .publisher(for: .touchUpInside) + .sink { [unowned self] in + didAcceptTerms = true + + if let ndf = ndf { + coordinator.presentUsername(ndf, self) + } else { + coordinator.presentChatList(self) + } + }.store(in: &cancellables) + + screenView.showTermsButton + .publisher(for: .touchUpInside) + .sink { _ in + // TODO + }.store(in: &cancellables) + } +} diff --git a/Sources/TermsFeature/TermsConditionsView.swift b/Sources/TermsFeature/TermsConditionsView.swift new file mode 100644 index 0000000000000000000000000000000000000000..fb7d1198f586cd12ecc3e1b0e1479c5cfba84a41 --- /dev/null +++ b/Sources/TermsFeature/TermsConditionsView.swift @@ -0,0 +1,72 @@ +import UIKit +import Shared + +final class TermsConditionsView: UIView { + let titleLabel = UILabel() + let nextButton = CapsuleButton() + let showTermsButton = CapsuleButton() + let radioComponent = RadioTextComponent() + + init() { + super.init(frame: .zero) + backgroundColor = Asset.neutralWhite.color + + let attString = NSMutableAttributedString(string: Localized.Terms.title) + let paragraph = NSMutableParagraphStyle() + paragraph.alignment = .left + paragraph.lineHeightMultiple = 1.15 + + attString.addAttribute(.paragraphStyle, value: paragraph) + attString.addAttribute(.foregroundColor, value: Asset.neutralActive.color) + attString.addAttribute(.font, value: Fonts.Mulish.bold.font(size: 34.0) as Any) + + attString.addAttributes(attributes: [ + .font: Fonts.Mulish.bold.font(size: 34.0) as Any, + .foregroundColor: Asset.brandPrimary.color + ], betweenCharacters: "#") + + titleLabel.numberOfLines = 0 + titleLabel.attributedText = attString + + radioComponent.titleLabel.text = Localized.Terms.radio + + nextButton.isEnabled = false + nextButton.set(style: .brandColored, title: Localized.Terms.accept) + showTermsButton.set(style: .seeThrough, title: Localized.Terms.show) + + addSubview(titleLabel) + addSubview(nextButton) + addSubview(radioComponent) + addSubview(showTermsButton) + + setupConstraints() + } + + required init?(coder: NSCoder) { nil } + + private func setupConstraints() { + titleLabel.snp.makeConstraints { + $0.top.equalTo(safeAreaLayoutGuide).offset(30) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-44) + } + + radioComponent.snp.makeConstraints { + $0.left.equalToSuperview().offset(40) + $0.right.equalToSuperview().offset(-40) + $0.bottom.equalTo(nextButton.snp.top).offset(-20) + } + + nextButton.snp.makeConstraints { + $0.left.equalToSuperview().offset(40) + $0.right.equalToSuperview().offset(-40) + $0.bottom.equalTo(showTermsButton.snp.top).offset(-10) + } + + showTermsButton.snp.makeConstraints { + $0.left.equalToSuperview().offset(40) + $0.right.equalToSuperview().offset(-40) + $0.bottom.equalTo(safeAreaLayoutGuide).offset(-40) + } + } +} diff --git a/Sources/TermsFeature/TermsCoordinator.swift b/Sources/TermsFeature/TermsCoordinator.swift new file mode 100644 index 0000000000000000000000000000000000000000..daff90e4b1b81b18e75e3a6e34557a3478e26f57 --- /dev/null +++ b/Sources/TermsFeature/TermsCoordinator.swift @@ -0,0 +1,25 @@ +import UIKit +import Presentation + +public struct TermsCoordinator { + var presentChatList: (UIViewController) -> Void + var presentUsername: (String, UIViewController) -> Void +} + +public extension TermsCoordinator { + static func live( + usernameFactory: @escaping (String) -> UIViewController, + chatListFactory: @escaping () -> UIViewController + ) -> Self { + .init( + presentChatList: { parent in + let presenter = ReplacePresenter() + presenter.present(chatListFactory(), from: parent) + }, + presentUsername: { ndf, parent in + let presenter = PushPresenter() + presenter.present(usernameFactory(ndf), from: parent) + } + ) + } +} diff --git a/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved b/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved index 2932261c8cc3c80fb2730138af5c8ff904e9334f..2edfffa23ebd11df1784b06aa0e734df8c56269a 100644 --- a/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://git.xx.network/elixxir/client-ios-db.git", "state" : { - "revision" : "785e1f653ee5eaaaf58a82c8abbcda2174fbc27a", - "version" : "1.0.8" + "revision" : "f8e3e0088de8301d6c4816e12f0aca1d6f02a280", + "version" : "1.1.0" } }, { @@ -350,6 +350,15 @@ "version" : "1.18.0" } }, + { + "identity" : "swiftcsv", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftcsv/SwiftCSV.git", + "state" : { + "revision" : "048a1d3c2950b9c151ef9364b36f91baadc2c28c", + "version" : "0.8.0" + } + }, { "identity" : "swiftybeaver", "kind" : "remoteSourceControl",