Skip to content
Snippets Groups Projects
Commit 817d15c1 authored by Bruno Muniz's avatar Bruno Muniz :apple:
Browse files

Finished adding search to pending invitation when opening the app

parent 21d1fe0a
No related branches found
No related tags found
3 merge requests!71Releasing v1.1.5 (214),!67v1.1.5 b(203),!59Invite friends
Showing
with 105 additions and 19 deletions
......@@ -34,10 +34,10 @@
</dict>
<dict>
<key>CFBundleURLName</key>
<string>xxmessenger</string>
<string>xxnetwork</string>
<key>CFBundleURLSchemes</key>
<array>
<string>xxmessenger</string>
<string>xxnetwork</string>
</array>
</dict>
<dict>
......
......@@ -20,11 +20,11 @@ public class AppDelegate: UIResponder, UIApplicationDelegate {
@Dependency private var crashReporter: CrashReporter
@Dependency private var dropboxService: DropboxInterface
@KeyObject(.invitation, defaultValue: nil) var invitation: String?
@KeyObject(.hideAppList, defaultValue: false) var hideAppList: Bool
@KeyObject(.recordingLogs, defaultValue: true) var recordingLogs: Bool
@KeyObject(.crashReporting, defaultValue: true) var isCrashReportingEnabled: Bool
var invitation: String?
var calledStopNetwork = false
var forceFailedPendingMessages = false
......@@ -134,12 +134,6 @@ public class AppDelegate: UIResponder, UIApplicationDelegate {
}
public func applicationDidBecomeActive(_ application: UIApplication) {
// TODO:
/// If an invitation is set -> navigate to
/// search screen and perform a search
///
invitation = nil
application.applicationIconBadgeNumber = 0
coverView?.removeFromSuperview()
}
......@@ -149,12 +143,14 @@ public class AppDelegate: UIResponder, UIApplicationDelegate {
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
if let host = url.host, host.starts(with: "invitation-") {
invitation = host.replacingOccurrences(of: "invitation-", with: "")
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let invitation = components.queryItems?.first(where: { $0.name == "invitation" }),
let username = invitation.value {
self.invitation = username
return true
} else {
return dropboxService.handleOpenUrl(url)
}
return dropboxService.handleOpenUrl(url)
}
}
......
......@@ -113,6 +113,7 @@ struct DependencyRegistrator {
container.register(
LaunchCoordinator(
searchFactory: SearchContainerController.init,
requestsFactory: RequestsContainerController.init,
chatListFactory: ChatListController.init,
onboardingFactory: OnboardingStartController.init(_:),
......
......@@ -21,6 +21,7 @@ public enum Key: String {
// MARK: General
case theme
case invitation
// MARK: Requests
......
import Retry
import Models
import Combine
import XXModels
import Foundation
import Combine
extension Session {
public func waitForNodes(timeout: Int) -> AnyPublisher<Void, Error> {
Deferred {
Future { promise in
retry(max: timeout, retryStrategy: .delay(seconds: 1)) { [weak self] in
guard let self = self else { return }
try self.client.bindings.nodeRegistrationStatus()
promise(.success(()))
}.finalCatch {
promise(.failure($0))
}
}
}.eraseToAnyPublisher()
}
public func search(fact: String) -> AnyPublisher<Contact, Error> {
Deferred {
Future { promise in
......
......@@ -68,4 +68,6 @@ public protocol SessionType {
)
func search(fact: String) -> AnyPublisher<Contact, Error>
func waitForNodes(timeout: Int) -> AnyPublisher<Void, Error>
}
......@@ -49,6 +49,8 @@ public final class LaunchController: UIViewController {
.receive(on: DispatchQueue.main)
.sink { [unowned self] in
switch $0 {
case .search:
coordinator.toSearch(from: self)
case .chats:
if let pushRoute = pendingPushRoute {
switch pushRoute {
......
......@@ -5,6 +5,7 @@ import Presentation
public protocol LaunchCoordinating {
func toChats(from: UIViewController)
func toSearch(from: UIViewController)
func toRequests(from: UIViewController)
func toOnboarding(with: String, from: UIViewController)
func toSingleChat(with: Contact, from: UIViewController)
......@@ -14,6 +15,7 @@ public protocol LaunchCoordinating {
public struct LaunchCoordinator: LaunchCoordinating {
var replacePresenter: Presenting = ReplacePresenter()
var searchFactory: () -> UIViewController
var requestsFactory: () -> UIViewController
var chatListFactory: () -> UIViewController
var onboardingFactory: (String) -> UIViewController
......@@ -21,12 +23,14 @@ public struct LaunchCoordinator: LaunchCoordinating {
var groupChatFactory: (GroupInfo) -> UIViewController
public init(
searchFactory: @escaping () -> UIViewController,
requestsFactory: @escaping () -> UIViewController,
chatListFactory: @escaping () -> UIViewController,
onboardingFactory: @escaping (String) -> UIViewController,
singleChatFactory: @escaping (Contact) -> UIViewController,
groupChatFactory: @escaping (GroupInfo) -> UIViewController
) {
self.searchFactory = searchFactory
self.requestsFactory = requestsFactory
self.chatListFactory = chatListFactory
self.groupChatFactory = groupChatFactory
......@@ -36,6 +40,12 @@ public struct LaunchCoordinator: LaunchCoordinating {
}
public extension LaunchCoordinator {
func toSearch(from parent: UIViewController) {
let screen = searchFactory()
let chatListScreen = chatListFactory()
replacePresenter.present(chatListScreen, screen, from: parent)
}
func toChats(from parent: UIViewController) {
let screen = chatListFactory()
replacePresenter.present(screen, from: parent)
......
......@@ -24,6 +24,7 @@ struct Update {
enum LaunchRoute {
case chats
case search
case update(Update)
case onboarding(String)
}
......@@ -36,6 +37,7 @@ final class LaunchViewModel {
@Dependency private var permissionHandler: PermissionHandling
@KeyObject(.username, defaultValue: nil) var username: String?
@KeyObject(.invitation, defaultValue: nil) var invitation: String?
@KeyObject(.biometrics, defaultValue: false) var isBiometricsOn: Bool
var hudPublisher: AnyPublisher<HUDStatus, Never> {
......@@ -180,17 +182,28 @@ final class LaunchViewModel {
private func checkBiometrics() {
if permissionHandler.isBiometricsAvailable && isBiometricsOn {
permissionHandler.requestBiometrics { [weak self] in
guard let self = self else { return }
switch $0 {
case .success(let granted):
guard granted else { return }
self?.routeSubject.send(.chats)
if self.invitation != nil {
self.routeSubject.send(.search)
} else {
self.routeSubject.send(.chats)
}
case .failure(let error):
self?.hudSubject.send(.error(HUDError(with: error)))
self.hudSubject.send(.error(HUDError(with: error)))
}
}
} else {
routeSubject.send(.chats)
if self.invitation != nil {
self.routeSubject.send(.search)
} else {
self.routeSubject.send(.chats)
}
}
}
}
......@@ -42,6 +42,6 @@ final class MenuViewModel {
}
var referralDeeplink: String {
"xxmessenger://invitation-\(username)"
"xxnetwork://messenger?invitation=\(username)"
}
}
......@@ -72,7 +72,7 @@ final class MenuView: UIView {
requestsButton.set(color: Asset.brandPrimary.color)
case .settings:
settingsButton.set(color: Asset.brandPrimary.color)
default:
case .share, .join, .profile, .dashboard:
break
}
}
......
......@@ -37,6 +37,11 @@ final class SearchLeftController: UIViewController {
setupBindings()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewModel.viewDidAppear()
}
func endEditing() {
screenView.inputField.endEditing(true)
}
......@@ -131,6 +136,13 @@ final class SearchLeftController: UIViewController {
.sink { [unowned self] in screenView.countryButton.setFlag($0.flag, prefix: $0.prefix) }
.store(in: &cancellables)
viewModel.statePublisher
.map(\.input)
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { [unowned self] in screenView.inputField.update(content: $0) }
.store(in: &cancellables)
viewModel.statePublisher
.compactMap(\.snapshot)
.receive(on: DispatchQueue.main)
......
......@@ -3,8 +3,10 @@ import UIKit
import Shared
import Combine
import XXModels
import Defaults
import Countries
import Integration
import NetworkMonitor
import DependencyInjection
typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem>
......@@ -18,6 +20,9 @@ struct SearchLeftViewState {
final class SearchLeftViewModel {
@Dependency var session: SessionType
@Dependency var networkMonitor: NetworkMonitoring
@KeyObject(.invitation, defaultValue: nil) var invitation: String?
var hudPublisher: AnyPublisher<HUDStatus, Never> {
hudSubject.eraseToAnyPublisher()
......@@ -35,6 +40,31 @@ final class SearchLeftViewModel {
private let successSubject = PassthroughSubject<Contact, Never>()
private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none)
private let stateSubject = CurrentValueSubject<SearchLeftViewState, Never>(.init())
private var networkCancellable = Set<AnyCancellable>()
func viewDidAppear() {
if let pendingInvitation = invitation {
invitation = nil
stateSubject.value.input = pendingInvitation
hudSubject.send(.onAction(Localized.Ud.Search.cancel))
networkMonitor.statusPublisher
.first { $0 == .available }
.map { [unowned self] _ in
session.waitForNodes(timeout: 5).first()
.sink {
if case .failure(let error) = $0 {
self.hudSubject.send(.error(.init(with: error)))
}
networkCancellable.removeAll()
} receiveValue: { _ in
self.didStartSearching()
networkCancellable.removeAll()
}.store(in: &networkCancellable)
}.sink(receiveValue: { _ in })
.store(in: &networkCancellable)
}
}
func didEnterInput(_ string: String) {
stateSubject.value.input = string
......
......@@ -115,6 +115,10 @@ public final class SearchComponent: UIView {
}
}
public func update(content: String) {
inputField.text = content
}
public func update(placeholder: String) {
inputField.attributedPlaceholder = NSAttributedString(
string: placeholder,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment