Commit 9ff96751 authored by Bruno Muniz's avatar Bruno Muniz 🍎
Browse files

Merge branch 'development' into 'master'

Releasing v1.1.5 (214)

See merge request !71
parents 731cef6d d074afa3
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1340"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ChatFeature"
BuildableName = "ChatFeature"
BlueprintName = "ChatFeature"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ChatFeatureTests"
BuildableName = "ChatFeatureTests"
BlueprintName = "ChatFeatureTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ChatFeature"
BuildableName = "ChatFeature"
BlueprintName = "ChatFeature"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
......@@ -52,6 +52,24 @@
migratedStopOnEveryIssue = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Script"
scriptText = "#!/bin/sh&#10;/usr/libexec/PlistBuddy -c &quot;Set :isReportingOptional YES&quot; &quot;${SRCROOT}/client-ios/Resources/Info.plist&quot;&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "02FDD06121EDA39A000F1286"
BuildableName = "client-ios.app"
BlueprintName = "client-ios"
ReferencedContainer = "container:client-ios.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
......@@ -91,5 +109,23 @@
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Script"
scriptText = "#!/bin/sh&#10;/usr/libexec/PlistBuddy -c &quot;Set :isReportingOptional NO&quot; &quot;${SRCROOT}/client-ios/Resources/Info.plist&quot;&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "02FDD06121EDA39A000F1286"
BuildableName = "client-ios.app"
BlueprintName = "client-ios"
ReferencedContainer = "container:client-ios.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
</ArchiveAction>
</Scheme>
......@@ -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>
......@@ -104,5 +104,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>isReportingOptional</key>
<false/>
</dict>
</plist>
......@@ -4,6 +4,10 @@
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:elixxir.io</string>
</array>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.xxm-cloud</string>
......
This diff is collapsed.
......@@ -144,6 +144,35 @@ public class AppDelegate: UIResponder, UIApplicationDelegate {
) -> Bool {
dropboxService.handleOpenUrl(url)
}
public func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let incomingURL = userActivity.webpageURL,
let username = getUsernameFromInvitationDeepLink(incomingURL) else {
return false
}
let router = try! DependencyInjection.Container.shared.resolve() as PushRouter
router.navigateTo(.search(username: username), {})
return true
}
}
func getUsernameFromInvitationDeepLink(_ url: URL) -> String? {
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
components.scheme == "https",
components.host == "elixxir.io",
components.path == "/connect",
let queryItem = components.queryItems?.first(where: { $0.name == "username" }),
let username = queryItem.value {
return username
}
return nil
}
// MARK: Notifications
......
......@@ -26,6 +26,7 @@ import CrashReporting
import NetworkMonitor
import DropboxFeature
import VersionChecking
import ReportingFeature
import GoogleDriveFeature
import DependencyInjection
......@@ -34,6 +35,7 @@ import DependencyInjection
import ScanFeature
import ChatFeature
import MenuFeature
import TermsFeature
import BackupFeature
import SearchFeature
import LaunchFeature
......@@ -79,6 +81,7 @@ struct DependencyRegistrator {
container.register(XXLogger.live())
container.register(CrashReporter.live)
container.register(VersionChecker.live())
container.register(ReportingStatus.live())
container.register(XXNetwork<BindingsClient>() as XXNetworking)
container.register(NetworkMonitor() as NetworkMonitoring)
......@@ -101,6 +104,11 @@ struct DependencyRegistrator {
static private func registerCommonDependencies() {
container.register(Voxophone())
container.register(BackupService())
container.register(MakeAppScreenshot.live)
container.register(SendReport.live)
container.register(FetchBannedList.live)
container.register(ProcessBannedList.live)
container.register(MakeReportDrawer.live)
// MARK: Isolated
......@@ -111,8 +119,17 @@ 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,
onboardingFactory: OnboardingStartController.init(_:),
......@@ -205,6 +222,7 @@ struct DependencyRegistrator {
searchFactory: SearchContainerController.init,
welcomeFactory: OnboardingWelcomeController.init,
chatListFactory: ChatListController.init,
termsFactory: TermsConditionsController.init(_:),
usernameFactory: OnboardingUsernameController.init(_:),
restoreListFactory: RestoreListController.init(_:),
successFactory: OnboardingSuccessController.init(_:),
......@@ -250,38 +268,3 @@ struct DependencyRegistrator {
) 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 .requests:
if !(navigationController.viewControllers.last is RequestsContainerController) {
navigationController.setViewControllers([RequestsContainerController()], animated: true)
}
case .search(username: let username):
if let _ = try? DependencyInjection.Container.shared.resolve() as SessionType,
!(navigationController.viewControllers.last is SearchContainerController) {
navigationController.setViewControllers([
ChatListController(),
SearchContainerController(username)
], 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()
}
}
}
......@@ -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)
}
}
......@@ -2,7 +2,6 @@ import UIKit
import Shared
import Combine
import InputField
import ScrollViewController
public final class BackupPassphraseController: UIViewController {
lazy private var screenView = BackupPassphraseView()
......@@ -21,7 +20,6 @@ public final class BackupPassphraseController: UIViewController {
private let cancelClosure: EmptyClosure
private let stringClosure: StringClosure
private var cancellables = Set<AnyCancellable>()
private let keyboardListener = KeyboardFrameChangeListener(notificationCenter: .default)
public init(
_ cancelClosure: @escaping EmptyClosure,
......@@ -35,66 +33,41 @@ public final class BackupPassphraseController: UIViewController {
required init?(coder: NSCoder) { nil }
public override func loadView() {
let view = UIView()
view.addSubview(screenView)
screenView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.left.equalToSuperview()
make.right.equalToSuperview()
make.bottom.equalToSuperview().offset(0)
}
self.view = view
view = screenView
}
public override func viewDidLoad() {
super.viewDidLoad()
setupKeyboard()
setupBindings()
screenView.continueButton.isEnabled = false
}
private func setupKeyboard() {
keyboardListener.keyboardFrameWillChange = { [weak self] keyboard in
guard let self = self else { return }
let inset = self.view.frame.height - self.view.convert(keyboard.frame, from: nil).minY
self.screenView.snp.updateConstraints {
$0.bottom.equalToSuperview().offset(-inset)
}
self.view.setNeedsLayout()
UIView.animate(withDuration: keyboard.animationDuration) {
self.view.layoutIfNeeded()
}
}
}
private func setupBindings() {
screenView.inputField.returnPublisher
.sink { [unowned self] in screenView.inputField.endEditing(true) }
.store(in: &cancellables)
screenView.cancelButton
.publisher(for: .touchUpInside)
.sink { [unowned self] in dismiss(animated: true) { self.cancelClosure() }}
.store(in: &cancellables)
screenView
.inputField
.returnPublisher
.sink { [unowned self] in
screenView.inputField.endEditing(true)
}.store(in: &cancellables)
screenView.inputField
screenView
.inputField
.textPublisher
.sink { [unowned self] in passphrase = $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.store(in: &cancellables)
.sink { [unowned self] in
passphrase = $0.trimmingCharacters(in: .whitespacesAndNewlines)
}.store(in: &cancellables)
screenView
.continueButton
.publisher(for: .touchUpInside)
.sink { [unowned self] in
dismiss(animated: true) { self.stringClosure(self.passphrase) }
}.store(in: &cancellables)
screenView.continueButton
screenView
.cancelButton
.publisher(for: .touchUpInside)
.sink { [unowned self] in
dismiss(animated: true) {
self.stringClosure(self.passphrase)
}
dismiss(animated: true) { self.cancelClosure() }
}.store(in: &cancellables)
}
}
import UIKit
import Shared
import Presentation
import ScrollViewController
public protocol BackupCoordinating {
func toDrawer(
......@@ -16,12 +17,18 @@ public protocol BackupCoordinating {
}
public struct BackupCoordinator: BackupCoordinating {
var bottomPresenter: Presenting = BottomPresenter()
var fullscreenPresenter: Presenting = FullscreenPresenter()
var passphraseFactory: (@escaping EmptyClosure, @escaping StringClosure) -> UIViewController
var passphraseFactory: (
@escaping EmptyClosure,
@escaping StringClosure
) -> UIViewController
public init(
passphraseFactory: @escaping (@escaping EmptyClosure, @escaping StringClosure) -> UIViewController
passphraseFactory: @escaping (
@escaping EmptyClosure,
@escaping StringClosure
) -> UIViewController
) {
self.passphraseFactory = passphraseFactory
}
......@@ -32,7 +39,8 @@ public extension BackupCoordinator {
_ screen: UIViewController,
from parent: UIViewController
) {
bottomPresenter.present(screen, from: parent)
let target = ScrollViewController.embedding(screen)
fullscreenPresenter.present(target, from: parent)
}
func toPassphrase(
......@@ -41,6 +49,21 @@ public extension BackupCoordinator {
passphraseClosure: @escaping StringClosure
) {
let screen = passphraseFactory(cancelClosure, passphraseClosure)
bottomPresenter.present(screen, from: parent)
let target = ScrollViewController.embedding(screen)
fullscreenPresenter.present(target, from: parent)
}
}
extension ScrollViewController {
static func embedding(_ viewController: UIViewController) -> ScrollViewController {
let scrollViewController = ScrollViewController()
scrollViewController.addChild(viewController)
scrollViewController.contentView = viewController.view
scrollViewController.wrapperView.handlesTouchesOutsideContent = false
scrollViewController.wrapperView.alignContentToBottom = true
scrollViewController.scrollView.bounces = false
viewController.didMove(toParent: scrollViewController)
return scrollViewController
}
}
......@@ -4,49 +4,62 @@ import InputField
final class BackupPassphraseView: UIView {
let titleLabel = UILabel()
let subtitleLabel = UILabel()
let inputField = InputField()
let stackView = UIStackView()
let continueButton = CapsuleButton()
let inputField = InputField()
let subtitleLabel = UILabel()
let cancelButton = CapsuleButton()
let continueButton = CapsuleButton()
init() {
super.init(frame: .zero)
setup()
}
required init?(coder: NSCoder) { nil }
private func setup() {
layer.cornerRadius = 40
backgroundColor = Asset.neutralWhite.color
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
subtitleLabel.numberOfLines = 0
titleLabel.textColor = Asset.neutralActive.color
subtitleLabel.textColor = Asset.neutralActive.color
setupInput()
setupLabels()