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

Merge branch 'invite-friends' into 'development'

Invite friends

See merge request elixxir/client-ios!59
parents 88a60d85 6b7ccd2d
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 228 additions and 55 deletions
...@@ -34,10 +34,10 @@ ...@@ -34,10 +34,10 @@
</dict> </dict>
<dict> <dict>
<key>CFBundleURLName</key> <key>CFBundleURLName</key>
<string>xxmessenger</string> <string>xxnetwork</string>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>xxmessenger</string> <string>xxnetwork</string>
</array> </array>
</dict> </dict>
<dict> <dict>
......
...@@ -722,6 +722,13 @@ let package = Package( ...@@ -722,6 +722,13 @@ let package = Package(
dependencies: ["DependencyInjection"] dependencies: ["DependencyInjection"]
), ),
// MARK: - AppTests
.testTarget(
name: "AppTests",
dependencies: ["App"]
),
// MARK: - ProfileFeatureTests // MARK: - ProfileFeatureTests
.testTarget( .testTarget(
......
...@@ -20,6 +20,7 @@ public class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -20,6 +20,7 @@ public class AppDelegate: UIResponder, UIApplicationDelegate {
@Dependency private var crashReporter: CrashReporter @Dependency private var crashReporter: CrashReporter
@Dependency private var dropboxService: DropboxInterface @Dependency private var dropboxService: DropboxInterface
@KeyObject(.invitation, defaultValue: nil) var invitation: String?
@KeyObject(.hideAppList, defaultValue: false) var hideAppList: Bool @KeyObject(.hideAppList, defaultValue: false) var hideAppList: Bool
@KeyObject(.recordingLogs, defaultValue: true) var recordingLogs: Bool @KeyObject(.recordingLogs, defaultValue: true) var recordingLogs: Bool
@KeyObject(.crashReporting, defaultValue: true) var isCrashReportingEnabled: Bool @KeyObject(.crashReporting, defaultValue: true) var isCrashReportingEnabled: Bool
...@@ -142,10 +143,30 @@ public class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -142,10 +143,30 @@ public class AppDelegate: UIResponder, UIApplicationDelegate {
open url: URL, open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:] options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool { ) -> Bool {
dropboxService.handleOpenUrl(url) if let username = getUsernameFromInvitationDeepLink(url) {
let router = try! DependencyInjection.Container.shared.resolve() as PushRouter
invitation = username
router.navigateTo(.search, {})
return true
} else {
return dropboxService.handleOpenUrl(url)
}
} }
} }
func getUsernameFromInvitationDeepLink(_ url: URL) -> String? {
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
components.scheme == "xxnetwork",
components.host == "messenger",
let queryItem = components.queryItems?.first(where: { $0.name == "invitation" }),
let username = queryItem.value {
return username
}
return nil
}
// MARK: Notifications // MARK: Notifications
extension AppDelegate: UNUserNotificationCenterDelegate { extension AppDelegate: UNUserNotificationCenterDelegate {
......
...@@ -113,6 +113,7 @@ struct DependencyRegistrator { ...@@ -113,6 +113,7 @@ struct DependencyRegistrator {
container.register( container.register(
LaunchCoordinator( LaunchCoordinator(
searchFactory: SearchContainerController.init,
requestsFactory: RequestsContainerController.init, requestsFactory: RequestsContainerController.init,
chatListFactory: ChatListController.init, chatListFactory: ChatListController.init,
onboardingFactory: OnboardingStartController.init(_:), onboardingFactory: OnboardingStartController.init(_:),
...@@ -250,38 +251,3 @@ struct DependencyRegistrator { ...@@ -250,38 +251,3 @@ struct DependencyRegistrator {
) as ChatListCoordinating) ) as ChatListCoordinating)
} }
} }
extension PushRouter {
static func live(navigationController: UINavigationController) -> PushRouter {
PushRouter { route, completion in
if let launchController = navigationController.viewControllers.last as? LaunchController {
launchController.pendingPushRoute = route
} else {
switch route {
case .requests:
if (navigationController.viewControllers.last as? RequestsContainerController) == nil {
navigationController.setViewControllers([RequestsContainerController()], animated: true)
}
case .contactChat(id: let id):
if let session = try? DependencyInjection.Container.shared.resolve() as SessionType,
let contact = try? session.dbManager.fetchContacts(.init(id: [id])).first {
navigationController.setViewControllers([
ChatListController(),
SingleChatController(contact)
], animated: true)
}
case .groupChat(id: let id):
if let session = try? DependencyInjection.Container.shared.resolve() as SessionType,
let info = try? session.dbManager.fetchGroupInfos(.init(groupId: id)).first {
navigationController.setViewControllers([
ChatListController(),
GroupChatController(info)
], animated: true)
}
}
}
completion()
}
}
}
import UIKit
import PushFeature
import Integration
import ChatFeature
import SearchFeature
import LaunchFeature
import ChatListFeature
import RequestsFeature
import DependencyInjection
extension PushRouter {
static func live(navigationController: UINavigationController) -> PushRouter {
PushRouter { route, completion in
if let launchController = navigationController.viewControllers.last as? LaunchController {
launchController.pendingPushRoute = route
} else {
switch route {
case .search:
if !(navigationController.viewControllers.last is SearchContainerController) {
navigationController.setViewControllers([
ChatListController(),
SearchContainerController()
], animated: true)
}
case .requests:
if !(navigationController.viewControllers.last is RequestsContainerController) {
navigationController.setViewControllers([RequestsContainerController()], animated: true)
}
case .contactChat(id: let id):
if let session = try? DependencyInjection.Container.shared.resolve() as SessionType,
let contact = try? session.dbManager.fetchContacts(.init(id: [id])).first {
navigationController.setViewControllers([
ChatListController(),
SingleChatController(contact)
], animated: true)
}
case .groupChat(id: let id):
if let session = try? DependencyInjection.Container.shared.resolve() as SessionType,
let info = try? session.dbManager.fetchGroupInfos(.init(groupId: id)).first {
navigationController.setViewControllers([
ChatListController(),
GroupChatController(info)
], animated: true)
}
}
}
completion()
}
}
}
...@@ -21,6 +21,7 @@ public enum Key: String { ...@@ -21,6 +21,7 @@ public enum Key: String {
// MARK: General // MARK: General
case theme case theme
case invitation
// MARK: Requests // MARK: Requests
......
import Retry
import Models import Models
import Combine
import XXModels import XXModels
import Foundation import Foundation
import Combine
extension Session { 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> { public func search(fact: String) -> AnyPublisher<Contact, Error> {
Deferred { Deferred {
Future { promise in Future { promise in
......
...@@ -68,4 +68,6 @@ public protocol SessionType { ...@@ -68,4 +68,6 @@ public protocol SessionType {
) )
func search(fact: String) -> AnyPublisher<Contact, Error> func search(fact: String) -> AnyPublisher<Contact, Error>
func waitForNodes(timeout: Int) -> AnyPublisher<Void, Error>
} }
...@@ -5,7 +5,6 @@ import Combine ...@@ -5,7 +5,6 @@ import Combine
import PushFeature import PushFeature
import DependencyInjection import DependencyInjection
public final class LaunchController: UIViewController { public final class LaunchController: UIViewController {
@Dependency private var hud: HUD @Dependency private var hud: HUD
@Dependency private var coordinator: LaunchCoordinating @Dependency private var coordinator: LaunchCoordinating
...@@ -50,9 +49,14 @@ public final class LaunchController: UIViewController { ...@@ -50,9 +49,14 @@ public final class LaunchController: UIViewController {
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [unowned self] in .sink { [unowned self] in
switch $0 { switch $0 {
case .search:
coordinator.toSearch(from: self)
case .chats: case .chats:
if let pushRoute = pendingPushRoute { if let pushRoute = pendingPushRoute {
switch pushRoute { switch pushRoute {
case .search:
coordinator.toSearch(from: self)
case .requests: case .requests:
coordinator.toRequests(from: self) coordinator.toRequests(from: self)
......
...@@ -5,6 +5,7 @@ import Presentation ...@@ -5,6 +5,7 @@ import Presentation
public protocol LaunchCoordinating { public protocol LaunchCoordinating {
func toChats(from: UIViewController) func toChats(from: UIViewController)
func toSearch(from: UIViewController)
func toRequests(from: UIViewController) func toRequests(from: UIViewController)
func toOnboarding(with: String, from: UIViewController) func toOnboarding(with: String, from: UIViewController)
func toSingleChat(with: Contact, from: UIViewController) func toSingleChat(with: Contact, from: UIViewController)
...@@ -14,6 +15,7 @@ public protocol LaunchCoordinating { ...@@ -14,6 +15,7 @@ public protocol LaunchCoordinating {
public struct LaunchCoordinator: LaunchCoordinating { public struct LaunchCoordinator: LaunchCoordinating {
var replacePresenter: Presenting = ReplacePresenter() var replacePresenter: Presenting = ReplacePresenter()
var searchFactory: () -> UIViewController
var requestsFactory: () -> UIViewController var requestsFactory: () -> UIViewController
var chatListFactory: () -> UIViewController var chatListFactory: () -> UIViewController
var onboardingFactory: (String) -> UIViewController var onboardingFactory: (String) -> UIViewController
...@@ -21,12 +23,14 @@ public struct LaunchCoordinator: LaunchCoordinating { ...@@ -21,12 +23,14 @@ public struct LaunchCoordinator: LaunchCoordinating {
var groupChatFactory: (GroupInfo) -> UIViewController var groupChatFactory: (GroupInfo) -> UIViewController
public init( public init(
searchFactory: @escaping () -> UIViewController,
requestsFactory: @escaping () -> UIViewController, requestsFactory: @escaping () -> UIViewController,
chatListFactory: @escaping () -> UIViewController, chatListFactory: @escaping () -> UIViewController,
onboardingFactory: @escaping (String) -> UIViewController, onboardingFactory: @escaping (String) -> UIViewController,
singleChatFactory: @escaping (Contact) -> UIViewController, singleChatFactory: @escaping (Contact) -> UIViewController,
groupChatFactory: @escaping (GroupInfo) -> UIViewController groupChatFactory: @escaping (GroupInfo) -> UIViewController
) { ) {
self.searchFactory = searchFactory
self.requestsFactory = requestsFactory self.requestsFactory = requestsFactory
self.chatListFactory = chatListFactory self.chatListFactory = chatListFactory
self.groupChatFactory = groupChatFactory self.groupChatFactory = groupChatFactory
...@@ -36,6 +40,12 @@ public struct LaunchCoordinator: LaunchCoordinating { ...@@ -36,6 +40,12 @@ public struct LaunchCoordinator: LaunchCoordinating {
} }
public extension LaunchCoordinator { 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) { func toChats(from parent: UIViewController) {
let screen = chatListFactory() let screen = chatListFactory()
replacePresenter.present(screen, from: parent) replacePresenter.present(screen, from: parent)
......
...@@ -24,6 +24,7 @@ struct Update { ...@@ -24,6 +24,7 @@ struct Update {
enum LaunchRoute { enum LaunchRoute {
case chats case chats
case search
case update(Update) case update(Update)
case onboarding(String) case onboarding(String)
} }
...@@ -36,6 +37,7 @@ final class LaunchViewModel { ...@@ -36,6 +37,7 @@ final class LaunchViewModel {
@Dependency private var permissionHandler: PermissionHandling @Dependency private var permissionHandler: PermissionHandling
@KeyObject(.username, defaultValue: nil) var username: String? @KeyObject(.username, defaultValue: nil) var username: String?
@KeyObject(.invitation, defaultValue: nil) var invitation: String?
@KeyObject(.biometrics, defaultValue: false) var isBiometricsOn: Bool @KeyObject(.biometrics, defaultValue: false) var isBiometricsOn: Bool
var hudPublisher: AnyPublisher<HUDStatus, Never> { var hudPublisher: AnyPublisher<HUDStatus, Never> {
...@@ -180,17 +182,28 @@ final class LaunchViewModel { ...@@ -180,17 +182,28 @@ final class LaunchViewModel {
private func checkBiometrics() { private func checkBiometrics() {
if permissionHandler.isBiometricsAvailable && isBiometricsOn { if permissionHandler.isBiometricsAvailable && isBiometricsOn {
permissionHandler.requestBiometrics { [weak self] in permissionHandler.requestBiometrics { [weak self] in
guard let self = self else { return }
switch $0 { switch $0 {
case .success(let granted): case .success(let granted):
guard granted else { return } 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): case .failure(let error):
self?.hudSubject.send(.error(HUDError(with: error))) self.hudSubject.send(.error(HUDError(with: error)))
} }
} }
} else { } else {
routeSubject.send(.chats) if self.invitation != nil {
self.routeSubject.send(.search)
} else {
self.routeSubject.send(.chats)
}
} }
} }
} }
...@@ -9,6 +9,7 @@ public enum MenuItem { ...@@ -9,6 +9,7 @@ public enum MenuItem {
case join case join
case scan case scan
case chats case chats
case share
case profile case profile
case contacts case contacts
case requests case requests
...@@ -171,6 +172,19 @@ public final class MenuController: UIViewController { ...@@ -171,6 +172,19 @@ public final class MenuController: UIViewController {
} }
}.store(in: &cancellables) }.store(in: &cancellables)
screenView.shareButton
.publisher(for: .touchUpInside)
.receive(on: DispatchQueue.main)
.sink { [unowned self] in
dismiss(animated: true) { [weak self] in
guard let self = self, self.previousItem != .share else { return }
self.coordinator.toActivityController(
with: [Localized.Menu.shareContent(self.viewModel.referralDeeplink)],
from: self.previousController
)
}
}.store(in: &cancellables)
viewModel.requestCount viewModel.requestCount
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak screenView] in screenView?.requestsButton.updateNotification($0) } .sink { [weak screenView] in screenView?.requestsButton.updateNotification($0) }
......
...@@ -4,9 +4,11 @@ import Presentation ...@@ -4,9 +4,11 @@ import Presentation
public protocol MenuCoordinating { public protocol MenuCoordinating {
func toFlow(_ item: MenuItem, from: UIViewController) func toFlow(_ item: MenuItem, from: UIViewController)
func toDrawer(_: UIViewController, from: UIViewController) func toDrawer(_: UIViewController, from: UIViewController)
func toActivityController(with: [Any], from: UIViewController)
} }
public struct MenuCoordinator: MenuCoordinating { public struct MenuCoordinator: MenuCoordinating {
var modalPresenter: Presenting = ModalPresenter()
var bottomPresenter: Presenting = BottomPresenter() var bottomPresenter: Presenting = BottomPresenter()
var replacePresenter: Presenting = ReplacePresenter() var replacePresenter: Presenting = ReplacePresenter()
...@@ -16,6 +18,8 @@ public struct MenuCoordinator: MenuCoordinating { ...@@ -16,6 +18,8 @@ public struct MenuCoordinator: MenuCoordinating {
var settingsFactory: () -> UIViewController var settingsFactory: () -> UIViewController
var contactsFactory: () -> UIViewController var contactsFactory: () -> UIViewController
var requestsFactory: () -> UIViewController var requestsFactory: () -> UIViewController
var activityControllerFactory: ([Any]) -> UIViewController
= { UIActivityViewController(activityItems: $0, applicationActivities: nil) }
public init( public init(
scanFactory: @escaping () -> UIViewController, scanFactory: @escaping () -> UIViewController,
...@@ -61,4 +65,9 @@ public extension MenuCoordinator { ...@@ -61,4 +65,9 @@ public extension MenuCoordinator {
replacePresenter.present(controller, from: parent) replacePresenter.present(controller, from: parent)
} }
func toActivityController(with items: [Any], from parent: UIViewController) {
let screen = activityControllerFactory(items)
modalPresenter.present(screen, from: parent)
}
} }
...@@ -40,4 +40,8 @@ final class MenuViewModel { ...@@ -40,4 +40,8 @@ final class MenuViewModel {
var version: String { var version: String {
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
} }
var referralDeeplink: String {
"xxnetwork://messenger?invitation=\(username)"
}
} }
...@@ -2,31 +2,33 @@ import UIKit ...@@ -2,31 +2,33 @@ import UIKit
import Shared import Shared
final class MenuView: UIView { final class MenuView: UIView {
let headerView = MenuHeaderView() let buildLabel = UILabel()
let versionLabel = UILabel()
let stackView = UIStackView() let stackView = UIStackView()
let xxdkVersionLabel = UILabel()
let infoStackView = UIStackView()
let headerView = MenuHeaderView()
let joinButton = MenuSectionButton()
let scanButton = MenuSectionButton() let scanButton = MenuSectionButton()
let shareButton = MenuSectionButton()
let chatsButton = MenuSectionButton() let chatsButton = MenuSectionButton()
let contactsButton = MenuSectionButton() let contactsButton = MenuSectionButton()
let requestsButton = MenuSectionButton() let requestsButton = MenuSectionButton()
let settingsButton = MenuSectionButton() let settingsButton = MenuSectionButton()
let dashboardButton = MenuSectionButton() let dashboardButton = MenuSectionButton()
let joinButton = MenuSectionButton()
let infoStackView = UIStackView()
let buildLabel = UILabel()
let versionLabel = UILabel()
let xxdkVersionLabel = UILabel()
init() { init() {
super.init(frame: .zero) super.init(frame: .zero)
backgroundColor = Asset.neutralDark.color backgroundColor = Asset.neutralDark.color
chatsButton.set(title: Localized.Menu.chats, image: Asset.menuChats.image)
scanButton.set(title: Localized.Menu.scan, image: Asset.menuScan.image) scanButton.set(title: Localized.Menu.scan, image: Asset.menuScan.image)
shareButton.set(title: Localized.Menu.share, image: Asset.menuShare.image)
chatsButton.set(title: Localized.Menu.chats, image: Asset.menuChats.image)
joinButton.set(title: Localized.Menu.join, image: Asset.permissionLogo.image)
requestsButton.set(title: Localized.Menu.requests, image: Asset.menuRequests.image) requestsButton.set(title: Localized.Menu.requests, image: Asset.menuRequests.image)
contactsButton.set(title: Localized.Menu.contacts, image: Asset.menuContacts.image) contactsButton.set(title: Localized.Menu.contacts, image: Asset.menuContacts.image)
settingsButton.set(title: Localized.Menu.settings, image: Asset.menuSettings.image) settingsButton.set(title: Localized.Menu.settings, image: Asset.menuSettings.image)
dashboardButton.set(title: Localized.Menu.dashboard, image: Asset.menuDashboard.image) dashboardButton.set(title: Localized.Menu.dashboard, image: Asset.menuDashboard.image)
joinButton.set(title: "Join xx network", image: Asset.permissionLogo.image)
stackView.addArrangedSubview(chatsButton) stackView.addArrangedSubview(chatsButton)
stackView.addArrangedSubview(contactsButton) stackView.addArrangedSubview(contactsButton)
...@@ -35,6 +37,7 @@ final class MenuView: UIView { ...@@ -35,6 +37,7 @@ final class MenuView: UIView {
stackView.addArrangedSubview(settingsButton) stackView.addArrangedSubview(settingsButton)
stackView.addArrangedSubview(dashboardButton) stackView.addArrangedSubview(dashboardButton)
stackView.addArrangedSubview(joinButton) stackView.addArrangedSubview(joinButton)
stackView.addArrangedSubview(shareButton)
infoStackView.spacing = 10 infoStackView.spacing = 10
infoStackView.axis = .vertical infoStackView.axis = .vertical
...@@ -59,17 +62,17 @@ final class MenuView: UIView { ...@@ -59,17 +62,17 @@ final class MenuView: UIView {
func select(item: MenuItem) { func select(item: MenuItem) {
switch item { switch item {
case .scan:
scanButton.set(color: Asset.brandPrimary.color)
case .chats: case .chats:
chatsButton.set(color: Asset.brandPrimary.color) chatsButton.set(color: Asset.brandPrimary.color)
case .contacts: case .contacts:
contactsButton.set(color: Asset.brandPrimary.color) contactsButton.set(color: Asset.brandPrimary.color)
case .requests: case .requests:
requestsButton.set(color: Asset.brandPrimary.color) requestsButton.set(color: Asset.brandPrimary.color)
case .scan:
scanButton.set(color: Asset.brandPrimary.color)
case .settings: case .settings:
settingsButton.set(color: Asset.brandPrimary.color) settingsButton.set(color: Asset.brandPrimary.color)
case .profile, .dashboard, .join: case .share, .join, .profile, .dashboard:
break break
} }
} }
......
...@@ -4,6 +4,7 @@ public struct PushRouter { ...@@ -4,6 +4,7 @@ public struct PushRouter {
public typealias NavigateTo = (Route, @escaping () -> Void) -> Void public typealias NavigateTo = (Route, @escaping () -> Void) -> Void
public enum Route { public enum Route {
case search
case requests case requests
case groupChat(id: Data) case groupChat(id: Data)
case contactChat(id: Data) case contactChat(id: Data)
......
...@@ -37,6 +37,11 @@ final class SearchLeftController: UIViewController { ...@@ -37,6 +37,11 @@ final class SearchLeftController: UIViewController {
setupBindings() setupBindings()
} }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewModel.viewDidAppear()
}
func endEditing() { func endEditing() {
screenView.inputField.endEditing(true) screenView.inputField.endEditing(true)
} }
...@@ -131,6 +136,13 @@ final class SearchLeftController: UIViewController { ...@@ -131,6 +136,13 @@ final class SearchLeftController: UIViewController {
.sink { [unowned self] in screenView.countryButton.setFlag($0.flag, prefix: $0.prefix) } .sink { [unowned self] in screenView.countryButton.setFlag($0.flag, prefix: $0.prefix) }
.store(in: &cancellables) .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 viewModel.statePublisher
.compactMap(\.snapshot) .compactMap(\.snapshot)
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
...@@ -403,7 +415,6 @@ final class SearchLeftController: UIViewController { ...@@ -403,7 +415,6 @@ final class SearchLeftController: UIViewController {
coordinator.toDrawer(drawer, from: self) coordinator.toDrawer(drawer, from: self)
} }
} }
extension SearchLeftController: UITableViewDelegate { extension SearchLeftController: UITableViewDelegate {
......
...@@ -3,8 +3,10 @@ import UIKit ...@@ -3,8 +3,10 @@ import UIKit
import Shared import Shared
import Combine import Combine
import XXModels import XXModels
import Defaults
import Countries import Countries
import Integration import Integration
import NetworkMonitor
import DependencyInjection import DependencyInjection
typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem> typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem>
...@@ -18,6 +20,9 @@ struct SearchLeftViewState { ...@@ -18,6 +20,9 @@ struct SearchLeftViewState {
final class SearchLeftViewModel { final class SearchLeftViewModel {
@Dependency var session: SessionType @Dependency var session: SessionType
@Dependency var networkMonitor: NetworkMonitoring
@KeyObject(.invitation, defaultValue: nil) var invitation: String?
var hudPublisher: AnyPublisher<HUDStatus, Never> { var hudPublisher: AnyPublisher<HUDStatus, Never> {
hudSubject.eraseToAnyPublisher() hudSubject.eraseToAnyPublisher()
...@@ -35,6 +40,29 @@ final class SearchLeftViewModel { ...@@ -35,6 +40,29 @@ final class SearchLeftViewModel {
private let successSubject = PassthroughSubject<Contact, Never>() private let successSubject = PassthroughSubject<Contact, Never>()
private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none) private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none)
private let stateSubject = CurrentValueSubject<SearchLeftViewState, Never>(.init()) 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))
networkCancellable.removeAll()
networkMonitor.statusPublisher
.first { $0 == .available }
.eraseToAnyPublisher()
.flatMap { _ in self.session.waitForNodes(timeout: 5) }
.sink {
if case .failure(let error) = $0 {
self.hudSubject.send(.error(.init(with: error)))
}
} receiveValue: {
self.didStartSearching()
}.store(in: &networkCancellable)
}
}
func didEnterInput(_ string: String) { func didEnterInput(_ string: String) {
stateSubject.value.input = string stateSubject.value.input = string
......
...@@ -70,6 +70,7 @@ public enum Asset { ...@@ -70,6 +70,7 @@ public enum Asset {
public static let menuRequests = ImageAsset(name: "menu_requests") public static let menuRequests = ImageAsset(name: "menu_requests")
public static let menuScan = ImageAsset(name: "menu_scan") public static let menuScan = ImageAsset(name: "menu_scan")
public static let menuSettings = ImageAsset(name: "menu_settings") public static let menuSettings = ImageAsset(name: "menu_settings")
public static let menuShare = ImageAsset(name: "menu_share")
public static let onboardingBackground = ImageAsset(name: "onboarding_background") public static let onboardingBackground = ImageAsset(name: "onboarding_background")
public static let onboardingBottomLogoStart = ImageAsset(name: "onboarding_bottom_logo_start") public static let onboardingBottomLogoStart = ImageAsset(name: "onboarding_bottom_logo_start")
public static let onboardingEmail = ImageAsset(name: "onboarding_email") public static let onboardingEmail = ImageAsset(name: "onboarding_email")
......
...@@ -633,6 +633,8 @@ public enum Localized { ...@@ -633,6 +633,8 @@ public enum Localized {
public static let contacts = Localized.tr("Localizable", "menu.contacts") public static let contacts = Localized.tr("Localizable", "menu.contacts")
/// Dashboard /// Dashboard
public static let dashboard = Localized.tr("Localizable", "menu.dashboard") public static let dashboard = Localized.tr("Localizable", "menu.dashboard")
/// Join xx network
public static let join = Localized.tr("Localizable", "menu.join")
/// Profile /// Profile
public static let profile = Localized.tr("Localizable", "menu.profile") public static let profile = Localized.tr("Localizable", "menu.profile")
/// Requests /// Requests
...@@ -641,6 +643,16 @@ public enum Localized { ...@@ -641,6 +643,16 @@ public enum Localized {
public static let scan = Localized.tr("Localizable", "menu.scan") public static let scan = Localized.tr("Localizable", "menu.scan")
/// Settings /// Settings
public static let settings = Localized.tr("Localizable", "menu.settings") public static let settings = Localized.tr("Localizable", "menu.settings")
/// Share my profile
public static let share = Localized.tr("Localizable", "menu.share")
/// Hi, I'm using xx messenger, you can download it here:
/// https://invite.xx.network
///
/// And you can add me using this link:
/// %@
public static func shareContent(_ p1: Any) -> String {
return Localized.tr("Localizable", "menu.shareContent", String(describing: p1))
}
/// Hello /// Hello
public static let title = Localized.tr("Localizable", "menu.title") public static let title = Localized.tr("Localizable", "menu.title")
/// Version %@ /// Version %@
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment