Refactoring

This commit is contained in:
Justin Mazzocchi 2020-09-11 19:50:42 -07:00
parent 2745f2470d
commit ceff3fd4c9
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
8 changed files with 91 additions and 110 deletions

View file

@ -0,0 +1,7 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
extension URL: Identifiable {
public var id: String { absoluteString }
}

View file

@ -13,7 +13,20 @@ public enum AccessTokenEndpoint {
code: String?,
redirectURI: String?
)
case accounts(username: String, email: String, password: String, reason: String?)
case accounts(Registration)
}
public extension AccessTokenEndpoint {
struct Registration {
public var username = ""
public var email = ""
public var password = ""
public var locale = "en"
public var reason = ""
public var agreement = false
public init() {}
}
}
extension AccessTokenEndpoint: Endpoint {
@ -56,15 +69,17 @@ extension AccessTokenEndpoint: Endpoint {
params["redirect_uri"] = redirectURI
return params
case let .accounts(username, email, password, reason):
case let .accounts(registration):
var params: [String: Any] = [
"username": username,
"email": email,
"password": password,
"locale": Locale.autoupdatingCurrent.languageCode ?? "en", // TODO: probably need to map
"agreement": true]
"username": registration.username,
"email": registration.email,
"password": registration.password,
"locale": registration.locale,
"agreement": registration.agreement]
params["reason"] = reason
if !registration.reason.isEmpty {
params["reason"] = registration.reason
}
return params
}

View file

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
D0030982250C6C8500EACB32 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0030981250C6C8500EACB32 /* URL+Extensions.swift */; };
D01F41D724F880C400D55A2D /* StatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D01F41D424F880C400D55A2D /* StatusTableViewCell.xib */; };
D01F41D824F880C400D55A2D /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D524F880C400D55A2D /* StatusTableViewCell.swift */; };
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */; };
@ -80,6 +81,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
D0030981250C6C8500EACB32 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; };
D01F41D424F880C400D55A2D /* StatusTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StatusTableViewCell.xib; sourceTree = "<group>"; };
D01F41D524F880C400D55A2D /* StatusTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusTableViewCell.swift; sourceTree = "<group>"; };
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchFallthroughTextView.swift; sourceTree = "<group>"; };
@ -302,6 +304,7 @@
D0C7D46A24F76169001EBDBB /* String+Extensions.swift */,
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */,
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */,
D0030981250C6C8500EACB32 /* URL+Extensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -505,6 +508,7 @@
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */,
D0BEB21124FA2A91001B0F04 /* EditFilterView.swift in Sources */,
D0030982250C6C8500EACB32 /* URL+Extensions.swift in Sources */,
D0C7D4C424F7616A001EBDBB /* AppDelegate.swift in Sources */,
D0C7D49924F7616A001EBDBB /* AddIdentityView.swift in Sources */,
D0C7D4C324F7616A001EBDBB /* MetatextApp.swift in Sources */,

View file

@ -0,0 +1,5 @@
// Copyright © 2020 Metabolist. All rights reserved.
import MastodonAPI
public typealias Registration = AccessTokenEndpoint.Registration

View file

@ -37,18 +37,12 @@ public extension AllIdentitiesService {
: nil)
}
func createIdentity(
id: UUID,
url: URL,
username: String,
email: String,
password: String,
reason: String?) -> AnyPublisher<Never, Error> {
func createIdentity(id: UUID, url: URL, registration: Registration) -> AnyPublisher<Never, Error> {
createIdentity(
id: id,
url: url,
authenticationPublisher: AuthenticationService(url: url, environment: environment)
.register(username: username, email: email, password: password, reason: reason))
.register(registration))
}
func deleteIdentity(id: UUID) -> AnyPublisher<Never, Error> {

View file

@ -29,10 +29,7 @@ extension AuthenticationService {
.eraseToAnyPublisher()
}
func register(username: String,
email: String,
password: String,
reason: String?) -> AnyPublisher<(AppAuthorization, AccessToken), Error> {
func register(_ registration: Registration) -> AnyPublisher<(AppAuthorization, AccessToken), Error> {
let authorization = appAuthorization()
.share()
@ -49,12 +46,7 @@ extension AuthenticationService {
.flatMap { accessToken -> AnyPublisher<AccessToken, Error> in
mastodonAPIClient.accessToken = accessToken.accessToken
return mastodonAPIClient.request(
AccessTokenEndpoint.accounts(
username: username,
email: email,
password: password,
reason: reason))
return mastodonAPIClient.request(AccessTokenEndpoint.accounts(registration))
}
.eraseToAnyPublisher()
})

View file

@ -3,7 +3,6 @@
import Combine
import Foundation
import Mastodon
import MastodonAPI
import ServiceLayer
public enum RegistrationError: Error {
@ -15,14 +14,9 @@ public final class RegistrationViewModel: ObservableObject {
public let serverRulesURL: URL
public let termsOfServiceURL: URL
@Published public var alertItem: AlertItem?
@Published public var username = ""
@Published public var email = ""
@Published public var password = ""
@Published public var registration = Registration()
@Published public var passwordConfirmation = ""
@Published public var reason = ""
@Published public var passwordsMatch = false
@Published public var agreement = false
@Published public private(set) var registerButtonEnabled = false
@Published public private(set) var registerDisabled = true
@Published public private(set) var registering = false
private let url: URL
@ -36,34 +30,27 @@ public final class RegistrationViewModel: ObservableObject {
self.termsOfServiceURL = url.appendingPathComponent("terms")
self.allIdentitiesService = allIdentitiesService
Publishers.CombineLatest4($username, $email, $password, $reason)
.map { username, email, password, reason in
!username.isEmpty
&& !email.isEmpty
&& !password.isEmpty
&& (!instance.approvalRequired || !reason.isEmpty)
$registration
.map {
$0.username.isEmpty
|| $0.email.isEmpty
|| $0.password.isEmpty
|| ($0.reason.isEmpty && instance.approvalRequired)
|| !$0.agreement
}
.combineLatest($agreement)
.map { $0 && $1 }
.assign(to: &$registerButtonEnabled)
.assign(to: &$registerDisabled)
}
}
public extension RegistrationViewModel {
func registerTapped() {
guard password == passwordConfirmation else {
guard registration.password == passwordConfirmation else {
alertItem = AlertItem(error: RegistrationError.passwordConfirmationMismatch)
return
}
allIdentitiesService.createIdentity(
id: UUID(),
url: url,
username: username,
email: email,
password: password,
reason: reason)
allIdentitiesService.createIdentity(id: UUID(), url: url, registration: registration)
.handleEvents(receiveSubscription: { [weak self] _ in self?.registering = true })
.mapError { error -> Error in
if error is URLError {

View file

@ -6,75 +6,52 @@ import ViewModels
struct RegistrationView: View {
@StateObject var viewModel: RegistrationViewModel
@State private var presentWebView = false
@State private var toReview = ToReview.serverRules
@State private var presentURL: URL?
var body: some View {
Form {
Section {
HStack {
TextField("registration.username", text: $viewModel.username)
.autocapitalization(.none)
.disableAutocorrection(true)
Text("@" + viewModel.instance.uri)
.foregroundColor(.secondary)
}
TextField("registration.email", text: $viewModel.email)
HStack {
TextField("registration.username", text: $viewModel.registration.username)
.autocapitalization(.none)
.disableAutocorrection(true)
.keyboardType(.emailAddress)
SecureField("registration.password", text: $viewModel.password)
SecureField("registration.password-confirmation", text: $viewModel.passwordConfirmation)
if viewModel.instance.approvalRequired {
VStack(alignment: .leading) {
Text("registration.reason-\(viewModel.instance.uri)")
TextEditor(text: $viewModel.reason)
}
}
Button("registration.server-rules") {
toReview = .serverRules
presentWebView = true
}
Button("registration.terms-of-service") {
toReview = .termsOfService
presentWebView = true
}
Toggle("registration.agree-to-server-rules-and-terms-of-service",
isOn: $viewModel.agreement)
Text("@" + viewModel.instance.uri)
.foregroundColor(.secondary)
}
Section {
Group {
if viewModel.registering {
ProgressView()
} else {
Button(viewModel.instance.approvalRequired
? "add-identity.request-invite"
: "add-identity.join",
action: viewModel.registerTapped)
.disabled(!viewModel.registerButtonEnabled)
}
TextField("registration.email", text: $viewModel.registration.email)
.autocapitalization(.none)
.disableAutocorrection(true)
.keyboardType(.emailAddress)
SecureField("registration.password", text: $viewModel.registration.password)
SecureField("registration.password-confirmation", text: $viewModel.passwordConfirmation)
if viewModel.instance.approvalRequired {
VStack(alignment: .leading) {
Text("registration.reason-\(viewModel.instance.uri)")
TextEditor(text: $viewModel.registration.reason)
}
.frame(maxWidth: .infinity, alignment: .center)
}
Button("registration.server-rules") {
presentURL = viewModel.serverRulesURL
}
Button("registration.terms-of-service") {
presentURL = viewModel.termsOfServiceURL
}
Toggle("registration.agree-to-server-rules-and-terms-of-service",
isOn: $viewModel.registration.agreement)
Group {
if viewModel.registering {
ProgressView()
} else {
Button(viewModel.instance.approvalRequired
? "add-identity.request-invite"
: "add-identity.join",
action: viewModel.registerTapped)
.disabled(viewModel.registerDisabled)
}
}
.frame(maxWidth: .infinity, alignment: .center)
}
.alertItem($viewModel.alertItem)
.sheet(isPresented: $presentWebView) { () -> SafariView in
let url: URL
switch toReview {
case .serverRules: url = viewModel.serverRulesURL
case .termsOfService: url = viewModel.termsOfServiceURL
}
return SafariView(url: url)
}
}
}
private extension RegistrationView {
enum ToReview {
case serverRules
case termsOfService
.sheet(item: $presentURL) { SafariView(url: $0) }
}
}