feat: add authentication scene

This commit is contained in:
CMK 2021-02-02 15:38:54 +08:00
parent 71de1ed9be
commit 0f8ad0c444
6 changed files with 136 additions and 4 deletions

View File

@ -51,6 +51,7 @@
DB8AF55725C137A8002E6C99 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55625C137A8002E6C99 /* HomeViewController.swift */; };
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; };
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */; };
DB98334725C8056600AD9700 /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -161,6 +162,7 @@
DB8AF55625C137A8002E6C99 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = "<group>"; };
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineIndex.swift; sourceTree = "<group>"; };
DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
DBF53F5F25C14E88008AAC7B /* Mastodon.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = Mastodon.xctestplan; path = Mastodon/Mastodon.xctestplan; sourceTree = "<group>"; };
DBF53F6025C14E9D008AAC7B /* MastodonSDK.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MastodonSDK.xctestplan; sourceTree = "<group>"; };
EC6E707B68A67DB08EC288FA /* Pods-MastodonTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.debug.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.debug.xcconfig"; sourceTree = "<group>"; };
@ -241,6 +243,7 @@
children = (
DB0140A625C40C0900F9F3CF /* PinBased */,
DB01409525C40B6700F9F3CF /* AuthenticationViewController.swift */,
DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */,
);
path = Authentication;
sourceTree = "<group>";
@ -791,6 +794,7 @@
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */,
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */,
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
DB98334725C8056600AD9700 /* AuthenticationViewModel.swift in Sources */,
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */,
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */,
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,

View File

@ -37,7 +37,7 @@ extension SceneCoordinator {
}
enum Scene {
case authentication(viewModel: AuthenticationViewModel)
}
}
@ -108,8 +108,12 @@ private extension SceneCoordinator {
func get(scene: Scene) -> UIViewController? {
let viewController: UIViewController?
// TODO:
viewController = nil
switch scene {
case .authentication(let viewModel):
let _viewController = AuthenticationViewController()
_viewController.viewModel = viewModel
viewController = _viewController
}
setupDependency(for: viewController as? NeedsDependency)

View File

@ -5,6 +5,76 @@
// Created by MainasuK Cirno on 2021/1/29.
//
import os.log
import UIKit
import Combine
final class AuthenticationViewController: UIViewController, NeedsDependency {
var disposeBag = Set<AnyCancellable>()
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var viewModel: AuthenticationViewModel!
let domainTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "example.com"
textField.autocapitalizationType = .none
textField.autocorrectionType = .no
textField.keyboardType = .URL
return textField
}()
private(set) lazy var signInBarButtonItem = UIBarButtonItem(title: "Sign In", style: .plain, target: self, action: #selector(AuthenticationViewController.signInBarButtonItemPressed(_:)))
}
extension AuthenticationViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "Authentication"
view.backgroundColor = .systemBackground
navigationItem.rightBarButtonItem = signInBarButtonItem
domainTextField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(domainTextField)
NSLayoutConstraint.activate([
domainTextField.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 8),
domainTextField.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor, constant: 8),
domainTextField.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor, constant: 8),
domainTextField.heightAnchor.constraint(equalToConstant: 44), // FIXME:
])
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: domainTextField)
.compactMap { notification in
guard let textField = notification.object as? UITextField? else { return nil }
return textField?.text ?? ""
}
.assign(to: \.value, on: viewModel.input)
.store(in: &disposeBag)
viewModel.isSignInButtonEnabled
.receive(on: DispatchQueue.main)
.assign(to: \.isEnabled, on: signInBarButtonItem)
.store(in: &disposeBag)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
domainTextField.becomeFirstResponder()
}
}
extension AuthenticationViewController {
@objc private func signInBarButtonItemPressed(_ sender: UIBarButtonItem) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
}
}

View File

@ -0,0 +1,48 @@
//
// AuthenticationViewModel.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021/2/1.
//
import Foundation
import Combine
final class AuthenticationViewModel {
var disposeBag = Set<AnyCancellable>()
// input
let input = CurrentValueSubject<String, Never>("")
// output
let domain = CurrentValueSubject<String?, Never>(nil)
let isSignInButtonEnabled = CurrentValueSubject<Bool, Never>(false)
init() {
input
.map { input in
let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
guard !trimmed.isEmpty else { return nil }
let urlString = trimmed.hasPrefix("https://") ? trimmed : "https://" + trimmed
guard let url = URL(string: urlString),
let host = url.host else {
return nil
}
let components = host.components(separatedBy: ".")
guard (components.filter { !$0.isEmpty }).count >= 2 else { return nil }
return host
}
.assign(to: \.value, on: domain)
.store(in: &disposeBag)
domain
.print()
.map { $0 != nil }
.assign(to: \.value, on: isSignInButtonEnabled)
.store(in: &disposeBag)
}
}

View File

@ -14,7 +14,6 @@ final class HomeViewController: UIViewController, NeedsDependency {
}
extension HomeViewController {
override func viewDidLoad() {

View File

@ -24,6 +24,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
self.coordinator = sceneCoordinator
sceneCoordinator.setup()
#if DEBUG
DispatchQueue.main.async {
let authenticationViewModel = AuthenticationViewModel()
sceneCoordinator.present(scene: .authentication(viewModel: authenticationViewModel), from: nil, transition: .modal(animated: false, completion: nil))
}
#endif
window.makeKeyAndVisible()
}