230 lines
9.4 KiB
Swift
230 lines
9.4 KiB
Swift
//
|
|
// MastodonRegisterView.swift
|
|
// Mastodon
|
|
//
|
|
// Created by MainasuK on 2022-4-27.
|
|
//
|
|
|
|
import UIKit
|
|
import SwiftUI
|
|
import MastodonLocalization
|
|
import MastodonSDK
|
|
import MastodonAsset
|
|
|
|
struct MastodonRegisterView: View {
|
|
|
|
@ObservedObject var viewModel: MastodonRegisterViewModel
|
|
|
|
@State var usernameRightViewWidth: CGFloat = 300
|
|
|
|
var body: some View {
|
|
ScrollView(.vertical) {
|
|
let margin: CGFloat = 16
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
TextField(L10n.Scene.Register.Input.DisplayName.placeholder.localizedCapitalized, text: $viewModel.name)
|
|
.textContentType(.name)
|
|
.disableAutocorrection(true)
|
|
.modifier(FormTextFieldModifier(validateState: viewModel.displayNameValidateState))
|
|
HStack {
|
|
Text("@")
|
|
.accessibilityHidden(true)
|
|
TextField(L10n.Scene.Register.Input.Username.placeholder.localizedCapitalized, text: $viewModel.username)
|
|
.textContentType(.username)
|
|
.autocapitalization(.none)
|
|
.disableAutocorrection(true)
|
|
.keyboardType(.asciiCapable)
|
|
.accessibilityLabel(viewModel.accessibilityLabelUsernameField)
|
|
Text("@\(viewModel.domain)")
|
|
.lineLimit(1)
|
|
.truncationMode(.middle)
|
|
.measureWidth { usernameRightViewWidth = $0 }
|
|
.frame(width: min(300.0, usernameRightViewWidth), alignment: .trailing)
|
|
.accessibilityHidden(true)
|
|
}
|
|
.modifier(FormTextFieldModifier(validateState: viewModel.usernameValidateState))
|
|
.environment(\.layoutDirection, .leftToRight) // force LTR
|
|
if let errorPrompt = viewModel.usernameErrorPrompt {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text(errorPrompt)
|
|
.font(Font(UIFontMetrics(forTextStyle: .caption1).scaledFont(for: .systemFont(ofSize: 13, weight: .regular))))
|
|
//FIXME: Better way than comparing strings
|
|
if errorPrompt == L10n.Scene.Register.Error.Reason.taken(L10n.Scene.Register.Error.Item.username) {
|
|
Button {
|
|
viewModel.usernameErrorPrompt = nil
|
|
viewModel.usernameValidateState = .empty
|
|
viewModel.username = L10n.Scene.Register.Input.Username.suggestion(viewModel.username)
|
|
} label: {
|
|
Text(L10n.Scene.Register.Input.Username.suggestion(viewModel.username))
|
|
.foregroundColor(Asset.Colors.Brand.blurple.swiftUIColor)
|
|
.font(Font(UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .bold))))
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TextField(L10n.Scene.Register.Input.Email.placeholder.localizedCapitalized, text: $viewModel.email)
|
|
.textContentType(.emailAddress)
|
|
.autocapitalization(.none)
|
|
.disableAutocorrection(true)
|
|
.keyboardType(.emailAddress)
|
|
.modifier(FormTextFieldModifier(validateState: viewModel.emailValidateState))
|
|
if let errorPrompt = viewModel.emailErrorPrompt {
|
|
Text(errorPrompt)
|
|
.modifier(FormFootnoteModifier())
|
|
}
|
|
|
|
}
|
|
.padding(.horizontal, margin)
|
|
.padding(.bottom, 32)
|
|
|
|
// Email & Password & Password hint
|
|
VStack(alignment: .leading, spacing: margin) {
|
|
SecureField(L10n.Scene.Register.Input.Password.placeholder.localizedCapitalized, text: $viewModel.password)
|
|
.textContentType(.newPassword)
|
|
.modifier(FormTextFieldModifier(validateState: viewModel.passwordValidateState))
|
|
SecureField(L10n.Scene.Register.Input.Password.confirmationPlaceholder.localizedCapitalized, text: $viewModel.passwordConfirmation)
|
|
.textContentType(.newPassword)
|
|
.modifier(FormTextFieldModifier(validateState: viewModel.passwordValidateState))
|
|
Text(L10n.Scene.Register.Input.Password.hint)
|
|
.modifier(FormFootnoteModifier(foregroundColor: .secondary))
|
|
if let errorPrompt = viewModel.passwordErrorPrompt {
|
|
Text(errorPrompt)
|
|
.modifier(FormFootnoteModifier())
|
|
}
|
|
}
|
|
.padding(.horizontal, margin)
|
|
.padding(.bottom, 22)
|
|
|
|
// Reason
|
|
if viewModel.approvalRequired {
|
|
VStack(alignment: .leading, spacing: 11) {
|
|
TextField(L10n.Scene.Register.Input.Invite.registrationUserInviteRequest.localizedCapitalized, text: $viewModel.reason)
|
|
.modifier(FormTextFieldModifier(validateState: viewModel.reasonValidateState))
|
|
if let errorPrompt = viewModel.reasonErrorPrompt {
|
|
Text(errorPrompt)
|
|
.modifier(FormFootnoteModifier())
|
|
}
|
|
}
|
|
.padding(.horizontal, margin)
|
|
}
|
|
|
|
Spacer()
|
|
.frame(minHeight: viewModel.bottomPaddingHeight)
|
|
}
|
|
.background(
|
|
Color(viewModel.backgroundColor)
|
|
.onTapGesture {
|
|
viewModel.endEditing.send()
|
|
}
|
|
)
|
|
}
|
|
|
|
struct FormTextFieldModifier: ViewModifier {
|
|
var validateState: MastodonRegisterViewModel.ValidateState
|
|
|
|
func body(content: Content) -> some View {
|
|
ZStack {
|
|
let borderColor: Color = {
|
|
switch validateState {
|
|
case .empty: return Color(Asset.Scene.Onboarding.textFieldBackground.color)
|
|
case .invalid: return Color(Asset.Colors.TextField.invalid.color.withAlphaComponent(0.25))
|
|
case .valid: return Color(Asset.Scene.Onboarding.textFieldBackground.color)
|
|
}
|
|
}()
|
|
|
|
borderColor
|
|
.cornerRadius(10)
|
|
|
|
content
|
|
.padding()
|
|
.background(borderColor)
|
|
.cornerRadius(10)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FormFootnoteModifier: ViewModifier {
|
|
var foregroundColor = Color(Asset.Colors.TextField.invalid.color)
|
|
func body(content: Content) -> some View {
|
|
content
|
|
.font(.footnote)
|
|
.foregroundColor(foregroundColor)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
struct WidthKey: PreferenceKey {
|
|
static let defaultValue: CGFloat = 0
|
|
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
value = nextValue()
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
func measureWidth(_ f: @escaping (CGFloat) -> ()) -> some View {
|
|
overlay(GeometryReader { proxy in
|
|
Color.clear.preference(key: WidthKey.self, value: proxy.size.width)
|
|
}
|
|
.onPreferenceChange(WidthKey.self, perform: f))
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
struct MastodonRegisterView_Previews: PreviewProvider {
|
|
static var viewModel: MastodonRegisterViewModel {
|
|
let domain = "mstdn.jp"
|
|
return MastodonRegisterViewModel(
|
|
context: .shared,
|
|
domain: domain,
|
|
authenticateInfo: AuthenticationViewModel.AuthenticateInfo(
|
|
domain: domain,
|
|
application: Mastodon.Entity.Application(
|
|
name: "Preview",
|
|
website: nil,
|
|
vapidKey: nil,
|
|
redirectURI: nil,
|
|
clientID: "",
|
|
clientSecret: ""
|
|
),
|
|
redirectURI: ""
|
|
)!,
|
|
instance: Mastodon.Entity.Instance(domain: "mstdn.jp"),
|
|
applicationToken: Mastodon.Entity.Token(
|
|
accessToken: "",
|
|
tokenType: "",
|
|
scope: "",
|
|
createdAt: Date()
|
|
)
|
|
)
|
|
}
|
|
|
|
static var previews: some View {
|
|
Group {
|
|
NavigationView {
|
|
MastodonRegisterView(viewModel: viewModel)
|
|
.navigationBarTitle(Text(""))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
NavigationView {
|
|
MastodonRegisterView(viewModel: viewModel)
|
|
.navigationBarTitle(Text(""))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
.preferredColorScheme(.dark)
|
|
NavigationView {
|
|
MastodonRegisterView(viewModel: viewModel)
|
|
.navigationBarTitle(Text(""))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
.environment(\.sizeCategory, .accessibilityExtraLarge)
|
|
NavigationView {
|
|
MastodonRegisterView(viewModel: viewModel)
|
|
.navigationBarTitle(Text(""))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|