mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-28 19:11:30 +00:00
Refactoring
This commit is contained in:
parent
2745f2470d
commit
ceff3fd4c9
8 changed files with 91 additions and 110 deletions
7
Extensions/URL+Extensions.swift
Normal file
7
Extensions/URL+Extensions.swift
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension URL: Identifiable {
|
||||||
|
public var id: String { absoluteString }
|
||||||
|
}
|
|
@ -13,7 +13,20 @@ public enum AccessTokenEndpoint {
|
||||||
code: String?,
|
code: String?,
|
||||||
redirectURI: 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 {
|
extension AccessTokenEndpoint: Endpoint {
|
||||||
|
@ -56,15 +69,17 @@ extension AccessTokenEndpoint: Endpoint {
|
||||||
params["redirect_uri"] = redirectURI
|
params["redirect_uri"] = redirectURI
|
||||||
|
|
||||||
return params
|
return params
|
||||||
case let .accounts(username, email, password, reason):
|
case let .accounts(registration):
|
||||||
var params: [String: Any] = [
|
var params: [String: Any] = [
|
||||||
"username": username,
|
"username": registration.username,
|
||||||
"email": email,
|
"email": registration.email,
|
||||||
"password": password,
|
"password": registration.password,
|
||||||
"locale": Locale.autoupdatingCurrent.languageCode ?? "en", // TODO: probably need to map
|
"locale": registration.locale,
|
||||||
"agreement": true]
|
"agreement": registration.agreement]
|
||||||
|
|
||||||
params["reason"] = reason
|
if !registration.reason.isEmpty {
|
||||||
|
params["reason"] = registration.reason
|
||||||
|
}
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* 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 */; };
|
D01F41D724F880C400D55A2D /* StatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D01F41D424F880C400D55A2D /* StatusTableViewCell.xib */; };
|
||||||
D01F41D824F880C400D55A2D /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D524F880C400D55A2D /* StatusTableViewCell.swift */; };
|
D01F41D824F880C400D55A2D /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D524F880C400D55A2D /* StatusTableViewCell.swift */; };
|
||||||
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */; };
|
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */; };
|
||||||
|
@ -80,6 +81,7 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference 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>"; };
|
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>"; };
|
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>"; };
|
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchFallthroughTextView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -302,6 +304,7 @@
|
||||||
D0C7D46A24F76169001EBDBB /* String+Extensions.swift */,
|
D0C7D46A24F76169001EBDBB /* String+Extensions.swift */,
|
||||||
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */,
|
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */,
|
||||||
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */,
|
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */,
|
||||||
|
D0030981250C6C8500EACB32 /* URL+Extensions.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -505,6 +508,7 @@
|
||||||
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
||||||
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */,
|
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */,
|
||||||
D0BEB21124FA2A91001B0F04 /* EditFilterView.swift in Sources */,
|
D0BEB21124FA2A91001B0F04 /* EditFilterView.swift in Sources */,
|
||||||
|
D0030982250C6C8500EACB32 /* URL+Extensions.swift in Sources */,
|
||||||
D0C7D4C424F7616A001EBDBB /* AppDelegate.swift in Sources */,
|
D0C7D4C424F7616A001EBDBB /* AppDelegate.swift in Sources */,
|
||||||
D0C7D49924F7616A001EBDBB /* AddIdentityView.swift in Sources */,
|
D0C7D49924F7616A001EBDBB /* AddIdentityView.swift in Sources */,
|
||||||
D0C7D4C324F7616A001EBDBB /* MetatextApp.swift in Sources */,
|
D0C7D4C324F7616A001EBDBB /* MetatextApp.swift in Sources */,
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import MastodonAPI
|
||||||
|
|
||||||
|
public typealias Registration = AccessTokenEndpoint.Registration
|
|
@ -37,18 +37,12 @@ public extension AllIdentitiesService {
|
||||||
: nil)
|
: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createIdentity(
|
func createIdentity(id: UUID, url: URL, registration: Registration) -> AnyPublisher<Never, Error> {
|
||||||
id: UUID,
|
|
||||||
url: URL,
|
|
||||||
username: String,
|
|
||||||
email: String,
|
|
||||||
password: String,
|
|
||||||
reason: String?) -> AnyPublisher<Never, Error> {
|
|
||||||
createIdentity(
|
createIdentity(
|
||||||
id: id,
|
id: id,
|
||||||
url: url,
|
url: url,
|
||||||
authenticationPublisher: AuthenticationService(url: url, environment: environment)
|
authenticationPublisher: AuthenticationService(url: url, environment: environment)
|
||||||
.register(username: username, email: email, password: password, reason: reason))
|
.register(registration))
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteIdentity(id: UUID) -> AnyPublisher<Never, Error> {
|
func deleteIdentity(id: UUID) -> AnyPublisher<Never, Error> {
|
||||||
|
|
|
@ -29,10 +29,7 @@ extension AuthenticationService {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func register(username: String,
|
func register(_ registration: Registration) -> AnyPublisher<(AppAuthorization, AccessToken), Error> {
|
||||||
email: String,
|
|
||||||
password: String,
|
|
||||||
reason: String?) -> AnyPublisher<(AppAuthorization, AccessToken), Error> {
|
|
||||||
let authorization = appAuthorization()
|
let authorization = appAuthorization()
|
||||||
.share()
|
.share()
|
||||||
|
|
||||||
|
@ -49,12 +46,7 @@ extension AuthenticationService {
|
||||||
.flatMap { accessToken -> AnyPublisher<AccessToken, Error> in
|
.flatMap { accessToken -> AnyPublisher<AccessToken, Error> in
|
||||||
mastodonAPIClient.accessToken = accessToken.accessToken
|
mastodonAPIClient.accessToken = accessToken.accessToken
|
||||||
|
|
||||||
return mastodonAPIClient.request(
|
return mastodonAPIClient.request(AccessTokenEndpoint.accounts(registration))
|
||||||
AccessTokenEndpoint.accounts(
|
|
||||||
username: username,
|
|
||||||
email: email,
|
|
||||||
password: password,
|
|
||||||
reason: reason))
|
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import Mastodon
|
||||||
import MastodonAPI
|
|
||||||
import ServiceLayer
|
import ServiceLayer
|
||||||
|
|
||||||
public enum RegistrationError: Error {
|
public enum RegistrationError: Error {
|
||||||
|
@ -15,14 +14,9 @@ public final class RegistrationViewModel: ObservableObject {
|
||||||
public let serverRulesURL: URL
|
public let serverRulesURL: URL
|
||||||
public let termsOfServiceURL: URL
|
public let termsOfServiceURL: URL
|
||||||
@Published public var alertItem: AlertItem?
|
@Published public var alertItem: AlertItem?
|
||||||
@Published public var username = ""
|
@Published public var registration = Registration()
|
||||||
@Published public var email = ""
|
|
||||||
@Published public var password = ""
|
|
||||||
@Published public var passwordConfirmation = ""
|
@Published public var passwordConfirmation = ""
|
||||||
@Published public var reason = ""
|
@Published public private(set) var registerDisabled = true
|
||||||
@Published public var passwordsMatch = false
|
|
||||||
@Published public var agreement = false
|
|
||||||
@Published public private(set) var registerButtonEnabled = false
|
|
||||||
@Published public private(set) var registering = false
|
@Published public private(set) var registering = false
|
||||||
|
|
||||||
private let url: URL
|
private let url: URL
|
||||||
|
@ -36,34 +30,27 @@ public final class RegistrationViewModel: ObservableObject {
|
||||||
self.termsOfServiceURL = url.appendingPathComponent("terms")
|
self.termsOfServiceURL = url.appendingPathComponent("terms")
|
||||||
self.allIdentitiesService = allIdentitiesService
|
self.allIdentitiesService = allIdentitiesService
|
||||||
|
|
||||||
Publishers.CombineLatest4($username, $email, $password, $reason)
|
$registration
|
||||||
.map { username, email, password, reason in
|
.map {
|
||||||
!username.isEmpty
|
$0.username.isEmpty
|
||||||
&& !email.isEmpty
|
|| $0.email.isEmpty
|
||||||
&& !password.isEmpty
|
|| $0.password.isEmpty
|
||||||
&& (!instance.approvalRequired || !reason.isEmpty)
|
|| ($0.reason.isEmpty && instance.approvalRequired)
|
||||||
|
|| !$0.agreement
|
||||||
}
|
}
|
||||||
.combineLatest($agreement)
|
.assign(to: &$registerDisabled)
|
||||||
.map { $0 && $1 }
|
|
||||||
.assign(to: &$registerButtonEnabled)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension RegistrationViewModel {
|
public extension RegistrationViewModel {
|
||||||
func registerTapped() {
|
func registerTapped() {
|
||||||
guard password == passwordConfirmation else {
|
guard registration.password == passwordConfirmation else {
|
||||||
alertItem = AlertItem(error: RegistrationError.passwordConfirmationMismatch)
|
alertItem = AlertItem(error: RegistrationError.passwordConfirmationMismatch)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
allIdentitiesService.createIdentity(
|
allIdentitiesService.createIdentity(id: UUID(), url: url, registration: registration)
|
||||||
id: UUID(),
|
|
||||||
url: url,
|
|
||||||
username: username,
|
|
||||||
email: email,
|
|
||||||
password: password,
|
|
||||||
reason: reason)
|
|
||||||
.handleEvents(receiveSubscription: { [weak self] _ in self?.registering = true })
|
.handleEvents(receiveSubscription: { [weak self] _ in self?.registering = true })
|
||||||
.mapError { error -> Error in
|
.mapError { error -> Error in
|
||||||
if error is URLError {
|
if error is URLError {
|
||||||
|
|
|
@ -6,75 +6,52 @@ import ViewModels
|
||||||
struct RegistrationView: View {
|
struct RegistrationView: View {
|
||||||
@StateObject var viewModel: RegistrationViewModel
|
@StateObject var viewModel: RegistrationViewModel
|
||||||
|
|
||||||
@State private var presentWebView = false
|
@State private var presentURL: URL?
|
||||||
@State private var toReview = ToReview.serverRules
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
HStack {
|
||||||
HStack {
|
TextField("registration.username", text: $viewModel.registration.username)
|
||||||
TextField("registration.username", text: $viewModel.username)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
Text("@" + viewModel.instance.uri)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
TextField("registration.email", text: $viewModel.email)
|
|
||||||
.autocapitalization(.none)
|
.autocapitalization(.none)
|
||||||
.disableAutocorrection(true)
|
.disableAutocorrection(true)
|
||||||
.keyboardType(.emailAddress)
|
Text("@" + viewModel.instance.uri)
|
||||||
SecureField("registration.password", text: $viewModel.password)
|
.foregroundColor(.secondary)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
Section {
|
TextField("registration.email", text: $viewModel.registration.email)
|
||||||
Group {
|
.autocapitalization(.none)
|
||||||
if viewModel.registering {
|
.disableAutocorrection(true)
|
||||||
ProgressView()
|
.keyboardType(.emailAddress)
|
||||||
} else {
|
SecureField("registration.password", text: $viewModel.registration.password)
|
||||||
Button(viewModel.instance.approvalRequired
|
SecureField("registration.password-confirmation", text: $viewModel.passwordConfirmation)
|
||||||
? "add-identity.request-invite"
|
if viewModel.instance.approvalRequired {
|
||||||
: "add-identity.join",
|
VStack(alignment: .leading) {
|
||||||
action: viewModel.registerTapped)
|
Text("registration.reason-\(viewModel.instance.uri)")
|
||||||
.disabled(!viewModel.registerButtonEnabled)
|
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)
|
.alertItem($viewModel.alertItem)
|
||||||
.sheet(isPresented: $presentWebView) { () -> SafariView in
|
.sheet(item: $presentURL) { SafariView(url: $0) }
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue