Commit 3184df5a authored by Bruno Muniz's avatar Bruno Muniz 🍎
Browse files

Merge branch 'feature/terms-and-conditions' into 'development'

Terms and condition feature

See merge request !68
parents c3e18f86 3befb002
......@@ -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(
......
......@@ -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(_:),
......
......@@ -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)
}
}
......@@ -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)
}
......
......@@ -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 {
......
......@@ -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)
......
......@@ -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 {
......
......@@ -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 {
......
......@@ -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) {
......
......@@ -21,6 +21,7 @@ public enum Key: String {
// MARK: General
case theme
case acceptedTerms
// MARK: Requests
......
......@@ -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:
......
......@@ -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)
......
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()
}
}
}
......@@ -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() {