Merge pull request #154 from tootsuite/fix/onboarding
Fix onboarding UI/UX issues
This commit is contained in:
commit
c456281e2e
|
@ -115,11 +115,11 @@ extension PickServerSection {
|
|||
guard let proxiedThumbnail = server.proxiedThumbnail,
|
||||
let url = URL(string: proxiedThumbnail) else {
|
||||
cell.thumbnailImageView.image = placeholderImage
|
||||
cell.thumbnailActivityIdicator.stopAnimating()
|
||||
cell.thumbnailActivityIndicator.stopAnimating()
|
||||
return
|
||||
}
|
||||
cell.thumbnailImageView.isHidden = false
|
||||
cell.thumbnailActivityIdicator.startAnimating()
|
||||
cell.thumbnailActivityIndicator.startAnimating()
|
||||
|
||||
cell.thumbnailImageView.af.setImage(
|
||||
withURL: url,
|
||||
|
@ -129,7 +129,7 @@ extension PickServerSection {
|
|||
completion: { [weak cell] response in
|
||||
switch response.result {
|
||||
case .success, .failure:
|
||||
cell?.thumbnailActivityIdicator.stopAnimating()
|
||||
cell?.thumbnailActivityIndicator.stopAnimating()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -91,6 +91,7 @@ internal enum Asset {
|
|||
}
|
||||
internal static let battleshipGrey = ColorAsset(name: "Colors/battleshipGrey")
|
||||
internal static let brandBlue = ColorAsset(name: "Colors/brand.blue")
|
||||
internal static let brandBlueDarken20 = ColorAsset(name: "Colors/brand.blue.darken.20")
|
||||
internal static let danger = ColorAsset(name: "Colors/danger")
|
||||
internal static let disabled = ColorAsset(name: "Colors/disabled")
|
||||
internal static let inactive = ColorAsset(name: "Colors/inactive")
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xB0",
|
||||
"green" : "0x73",
|
||||
"red" : "0x1F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC9",
|
||||
"green" : "0x80",
|
||||
"red" : "0x1B"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ final class MastodonConfirmEmailViewController: UIViewController, NeedsDependenc
|
|||
|
||||
let largeTitleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.boldSystemFont(ofSize: 34))
|
||||
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.systemFont(ofSize: 34, weight: .bold))
|
||||
label.textColor = .label
|
||||
label.text = L10n.Scene.ConfirmEmail.title
|
||||
return label
|
||||
|
@ -45,14 +45,8 @@ final class MastodonConfirmEmailViewController: UIViewController, NeedsDependenc
|
|||
}()
|
||||
|
||||
let openEmailButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
|
||||
button.setBackgroundImage(UIImage.placeholder(color: Asset.Colors.brandBlue.color), for: .normal)
|
||||
button.setTitleColor(.white, for: .normal)
|
||||
let button = PrimaryActionButton()
|
||||
button.setTitle(L10n.Scene.ConfirmEmail.Button.openEmailApp, for: .normal)
|
||||
button.layer.masksToBounds = true
|
||||
button.layer.cornerRadius = 8
|
||||
button.layer.cornerCurve = .continuous
|
||||
button.addTarget(self, action: #selector(openEmailButtonPressed(_:)), for: UIControl.Event.touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
|
|
@ -237,21 +237,23 @@ extension MastodonPickServerViewController {
|
|||
.store(in: &disposeBag)
|
||||
|
||||
viewModel.emptyStateViewState
|
||||
.receive(on: DispatchQueue.main)
|
||||
.receive(on: RunLoop.main)
|
||||
.sink { [weak self] state in
|
||||
guard let self = self else { return }
|
||||
switch state {
|
||||
case .none:
|
||||
self.emptyStateView.isHidden = true
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
self.emptyStateView.alpha = 0
|
||||
}
|
||||
case .loading:
|
||||
self.emptyStateView.isHidden = false
|
||||
self.emptyStateView.alpha = 1
|
||||
self.emptyStateView.networkIndicatorImageView.isHidden = true
|
||||
self.emptyStateView.activityIndicatorView.startAnimating()
|
||||
self.emptyStateView.infoLabel.isHidden = false
|
||||
self.emptyStateView.infoLabel.text = L10n.Scene.ServerPicker.EmptyState.findingServers
|
||||
self.emptyStateView.infoLabel.textAlignment = self.traitCollection.layoutDirection == .rightToLeft ? .right : .left
|
||||
case .badNetwork:
|
||||
self.emptyStateView.isHidden = false
|
||||
self.emptyStateView.alpha = 1
|
||||
self.emptyStateView.networkIndicatorImageView.isHidden = false
|
||||
self.emptyStateView.activityIndicatorView.stopAnimating()
|
||||
self.emptyStateView.infoLabel.isHidden = false
|
||||
|
|
|
@ -45,9 +45,10 @@ extension MastodonPickServerViewModel.LoadIndexedServerState {
|
|||
viewModel.context.apiService.servers(language: nil, category: nil)
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure:
|
||||
case .failure(let error):
|
||||
// TODO: handle error
|
||||
stateMachine.enter(Fail.self)
|
||||
viewModel.loadingIndexedServersError.value = error
|
||||
case .finished:
|
||||
break
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ class MastodonPickServerViewModel: NSObject {
|
|||
let error = CurrentValueSubject<Error?, Never>(nil)
|
||||
|
||||
let isLoadingIndexedServers = CurrentValueSubject<Bool, Never>(false)
|
||||
let loadingIndexedServersError = CurrentValueSubject<Error?, Never>(nil)
|
||||
let emptyStateViewState = CurrentValueSubject<EmptyStateViewState, Never>(.none)
|
||||
|
||||
init(context: AppContext, mode: PickServerMode) {
|
||||
|
@ -142,16 +143,23 @@ extension MastodonPickServerViewModel {
|
|||
})
|
||||
.store(in: &disposeBag)
|
||||
|
||||
isLoadingIndexedServers
|
||||
.map { isLoadingIndexedServers -> EmptyStateViewState in
|
||||
if isLoadingIndexedServers {
|
||||
return .loading
|
||||
Publishers.CombineLatest(
|
||||
isLoadingIndexedServers,
|
||||
loadingIndexedServersError
|
||||
)
|
||||
.map { isLoadingIndexedServers, loadingIndexedServersError -> EmptyStateViewState in
|
||||
if isLoadingIndexedServers {
|
||||
if loadingIndexedServersError != nil {
|
||||
return .badNetwork
|
||||
} else {
|
||||
return .none
|
||||
return .loading
|
||||
}
|
||||
} else {
|
||||
return .none
|
||||
}
|
||||
.assign(to: \.value, on: emptyStateViewState)
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
.assign(to: \.value, on: emptyStateViewState)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
Publishers.CombineLatest3(
|
||||
indexedServers.eraseToAnyPublisher(),
|
||||
|
|
|
@ -60,7 +60,7 @@ class PickServerCell: UITableViewCell {
|
|||
return label
|
||||
}()
|
||||
|
||||
let thumbnailActivityIdicator = UIActivityIndicatorView(style: .medium)
|
||||
let thumbnailActivityIndicator = UIActivityIndicatorView(style: .medium)
|
||||
|
||||
let thumbnailImageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
|
@ -99,7 +99,7 @@ class PickServerCell: UITableViewCell {
|
|||
return button
|
||||
}()
|
||||
|
||||
let seperator: UIView = {
|
||||
let separator: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -177,7 +177,7 @@ class PickServerCell: UITableViewCell {
|
|||
|
||||
thumbnailImageView.isHidden = false
|
||||
thumbnailImageView.af.cancelImageRequest()
|
||||
thumbnailActivityIdicator.stopAnimating()
|
||||
thumbnailActivityIndicator.stopAnimating()
|
||||
disposeBag.removeAll()
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ extension PickServerCell {
|
|||
containerView.addSubview(domainLabel)
|
||||
containerView.addSubview(checkbox)
|
||||
containerView.addSubview(descriptionLabel)
|
||||
containerView.addSubview(seperator)
|
||||
containerView.addSubview(separator)
|
||||
|
||||
containerView.addSubview(expandButton)
|
||||
|
||||
|
@ -231,13 +231,13 @@ extension PickServerCell {
|
|||
containerView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||
containerView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
|
||||
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
||||
contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 1),
|
||||
contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
||||
|
||||
// Set bottom separator
|
||||
seperator.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
containerView.trailingAnchor.constraint(equalTo: seperator.trailingAnchor),
|
||||
containerView.topAnchor.constraint(equalTo: seperator.topAnchor),
|
||||
seperator.heightAnchor.constraint(equalToConstant: 1).priority(.defaultHigh),
|
||||
separator.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
containerView.trailingAnchor.constraint(equalTo: separator.trailingAnchor),
|
||||
containerView.topAnchor.constraint(equalTo: separator.topAnchor),
|
||||
separator.heightAnchor.constraint(equalToConstant: 1).priority(.defaultHigh),
|
||||
|
||||
domainLabel.topAnchor.constraint(equalTo: containerView.layoutMarginsGuide.topAnchor),
|
||||
domainLabel.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor),
|
||||
|
@ -272,14 +272,14 @@ extension PickServerCell {
|
|||
containerView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: expandButton.bottomAnchor),
|
||||
])
|
||||
|
||||
thumbnailActivityIdicator.translatesAutoresizingMaskIntoConstraints = false
|
||||
thumbnailImageView.addSubview(thumbnailActivityIdicator)
|
||||
thumbnailActivityIndicator.translatesAutoresizingMaskIntoConstraints = false
|
||||
thumbnailImageView.addSubview(thumbnailActivityIndicator)
|
||||
NSLayoutConstraint.activate([
|
||||
thumbnailActivityIdicator.centerXAnchor.constraint(equalTo: thumbnailImageView.centerXAnchor),
|
||||
thumbnailActivityIdicator.centerYAnchor.constraint(equalTo: thumbnailImageView.centerYAnchor),
|
||||
thumbnailActivityIndicator.centerXAnchor.constraint(equalTo: thumbnailImageView.centerXAnchor),
|
||||
thumbnailActivityIndicator.centerYAnchor.constraint(equalTo: thumbnailImageView.centerYAnchor),
|
||||
])
|
||||
thumbnailActivityIdicator.hidesWhenStopped = true
|
||||
thumbnailActivityIdicator.stopAnimating()
|
||||
thumbnailActivityIndicator.hidesWhenStopped = true
|
||||
thumbnailActivityIndicator.stopAnimating()
|
||||
|
||||
NSLayoutConstraint.activate(collapseConstraints)
|
||||
|
||||
|
|
|
@ -41,13 +41,44 @@ class PickServerSearchCell: UITableViewCell {
|
|||
let searchTextField: UITextField = {
|
||||
let textField = UITextField()
|
||||
textField.translatesAutoresizingMaskIntoConstraints = false
|
||||
textField.font = .preferredFont(forTextStyle: .headline)
|
||||
textField.leftView = {
|
||||
let imageView = UIImageView(
|
||||
image: UIImage(
|
||||
systemName: "magnifyingglass",
|
||||
withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .regular)
|
||||
)
|
||||
)
|
||||
imageView.tintColor = Asset.Colors.Label.secondary.color.withAlphaComponent(0.6)
|
||||
|
||||
let containerView = UIView()
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.addSubview(imageView)
|
||||
NSLayoutConstraint.activate([
|
||||
imageView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
imageView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
imageView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
||||
])
|
||||
|
||||
let paddingView = UIView()
|
||||
paddingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.addSubview(paddingView)
|
||||
NSLayoutConstraint.activate([
|
||||
paddingView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
paddingView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor),
|
||||
paddingView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
||||
paddingView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
||||
paddingView.widthAnchor.constraint(equalToConstant: 4).priority(.defaultHigh),
|
||||
])
|
||||
return containerView
|
||||
}()
|
||||
textField.leftViewMode = .always
|
||||
textField.font = .systemFont(ofSize: 15, weight: .regular)
|
||||
textField.tintColor = Asset.Colors.Label.primary.color
|
||||
textField.textColor = Asset.Colors.Label.primary.color
|
||||
textField.adjustsFontForContentSizeCategory = true
|
||||
textField.attributedPlaceholder =
|
||||
NSAttributedString(string: L10n.Scene.ServerPicker.Input.placeholder,
|
||||
attributes: [.font: UIFont.preferredFont(forTextStyle: .headline),
|
||||
attributes: [.font: UIFont.systemFont(ofSize: 15, weight: .regular),
|
||||
.foregroundColor: Asset.Colors.Label.secondary.color.withAlphaComponent(0.6)])
|
||||
textField.clearButtonMode = .whileEditing
|
||||
textField.autocapitalizationType = .none
|
||||
|
|
|
@ -76,22 +76,19 @@ extension PickServerEmptyStateView {
|
|||
])
|
||||
containerStackView.addArrangedSubview(networkIndicatorImageView)
|
||||
|
||||
let infoContainerView = UIView()
|
||||
activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
|
||||
infoContainerView.addSubview(activityIndicatorView)
|
||||
NSLayoutConstraint.activate([
|
||||
activityIndicatorView.leadingAnchor.constraint(equalTo: infoContainerView.leadingAnchor),
|
||||
activityIndicatorView.centerYAnchor.constraint(equalTo: infoContainerView.centerYAnchor),
|
||||
activityIndicatorView.bottomAnchor.constraint(equalTo: infoContainerView.bottomAnchor),
|
||||
])
|
||||
infoLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
infoContainerView.addSubview(infoLabel)
|
||||
NSLayoutConstraint.activate([
|
||||
infoLabel.leadingAnchor.constraint(equalTo: activityIndicatorView.trailingAnchor, constant: 4),
|
||||
infoLabel.centerYAnchor.constraint(equalTo: infoContainerView.centerYAnchor),
|
||||
infoLabel.trailingAnchor.constraint(equalTo: infoContainerView.trailingAnchor),
|
||||
])
|
||||
containerStackView.addArrangedSubview(infoContainerView)
|
||||
let infoContainerStackView = UIStackView()
|
||||
infoContainerStackView.axis = .horizontal
|
||||
infoContainerStackView.distribution = .fill
|
||||
|
||||
infoContainerStackView.addArrangedSubview(activityIndicatorView)
|
||||
infoContainerStackView.spacing = 4
|
||||
activityIndicatorView.setContentHuggingPriority(.required - 1, for: .horizontal)
|
||||
|
||||
infoContainerStackView.addArrangedSubview(infoLabel)
|
||||
infoLabel.setContentCompressionResistancePriority(.required - 1, for: .vertical)
|
||||
infoLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal)
|
||||
|
||||
containerStackView.addArrangedSubview(infoContainerStackView)
|
||||
|
||||
let bottomPaddingView = UIView()
|
||||
bottomPaddingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -104,7 +101,7 @@ extension PickServerEmptyStateView {
|
|||
])
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
bottomPaddingView.heightAnchor.constraint(equalTo: topPaddingView.heightAnchor, multiplier: 2.0),
|
||||
bottomPaddingView.heightAnchor.constraint(equalTo: topPaddingView.heightAnchor, multiplier: 1.0).priority(.defaultHigh),
|
||||
])
|
||||
|
||||
activityIndicatorView.hidesWhenStopped = true
|
||||
|
@ -126,15 +123,18 @@ struct PickServerEmptyStateView_Previews: PreviewProvider {
|
|||
emptyStateView.activityIndicatorView.stopAnimating()
|
||||
return emptyStateView
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 400))
|
||||
.previewLayout(.fixed(width: 375, height: 150))
|
||||
.previewDisplayName("Bad Network")
|
||||
UIViewPreview(width: 375) {
|
||||
let emptyStateView = PickServerEmptyStateView()
|
||||
emptyStateView.networkIndicatorImageView.isHidden = true
|
||||
emptyStateView.infoLabel.text = L10n.Scene.ServerPicker.EmptyState.findingServers
|
||||
emptyStateView.infoLabel.textAlignment = UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft ? .right : .left
|
||||
emptyStateView.activityIndicatorView.startAnimating()
|
||||
return emptyStateView
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 400))
|
||||
.previewLayout(.fixed(width: 375, height: 44))
|
||||
.previewDisplayName("Loading…")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
|
||||
let largeTitleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.boldSystemFont(ofSize: 34))
|
||||
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold))
|
||||
label.textColor = Asset.Colors.Label.primary.color
|
||||
label.text = L10n.Scene.Register.title
|
||||
label.numberOfLines = 0
|
||||
|
@ -93,11 +93,10 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
|
||||
let plusIconImageView: UIImageView = {
|
||||
let icon = UIImageView()
|
||||
|
||||
let image = Asset.Circles.plusCircleFill.image.withRenderingMode(.alwaysTemplate)
|
||||
icon.image = image
|
||||
icon.tintColor = Asset.Colors.Icon.plus.color
|
||||
icon.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color
|
||||
icon.backgroundColor = .white
|
||||
return icon
|
||||
}()
|
||||
|
||||
|
@ -110,7 +109,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
|
||||
let usernameTextField: UITextField = {
|
||||
let textField = UITextField()
|
||||
|
||||
textField.returnKeyType = .next
|
||||
textField.autocapitalizationType = .none
|
||||
textField.autocorrectionType = .no
|
||||
textField.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color
|
||||
|
@ -119,8 +118,35 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
|
||||
NSAttributedString.Key.font: MastodonRegisterViewController.textFieldLabelFont])
|
||||
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
||||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||
textField.leftView = paddingView
|
||||
textField.font = MastodonRegisterViewController.textFieldLabelFont
|
||||
textField.leftView = {
|
||||
let containerView = UIView()
|
||||
|
||||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||
paddingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.addSubview(paddingView)
|
||||
NSLayoutConstraint.activate([
|
||||
paddingView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
paddingView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
paddingView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
||||
paddingView.widthAnchor.constraint(equalToConstant: 5).priority(.defaultHigh),
|
||||
])
|
||||
|
||||
let label = UILabel()
|
||||
label.font = MastodonRegisterViewController.textFieldLabelFont
|
||||
label.textColor = Asset.Colors.Label.primary.color
|
||||
label.text = " @"
|
||||
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.addSubview(label)
|
||||
NSLayoutConstraint.activate([
|
||||
label.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
label.leadingAnchor.constraint(equalTo: paddingView.trailingAnchor),
|
||||
label.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
||||
label.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
||||
])
|
||||
return containerView
|
||||
}()
|
||||
textField.leftViewMode = .always
|
||||
return textField
|
||||
}()
|
||||
|
@ -134,6 +160,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
|
||||
let displayNameTextField: UITextField = {
|
||||
let textField = UITextField()
|
||||
textField.returnKeyType = .next
|
||||
textField.autocapitalizationType = .none
|
||||
textField.autocorrectionType = .no
|
||||
textField.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color
|
||||
|
@ -145,11 +172,13 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||
textField.leftView = paddingView
|
||||
textField.leftViewMode = .always
|
||||
textField.font = MastodonRegisterViewController.textFieldLabelFont
|
||||
return textField
|
||||
}()
|
||||
|
||||
let emailTextField: UITextField = {
|
||||
let textField = UITextField()
|
||||
textField.returnKeyType = .next
|
||||
textField.autocapitalizationType = .none
|
||||
textField.autocorrectionType = .no
|
||||
textField.keyboardType = .emailAddress
|
||||
|
@ -162,6 +191,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||
textField.leftView = paddingView
|
||||
textField.leftViewMode = .always
|
||||
textField.font = MastodonRegisterViewController.textFieldLabelFont
|
||||
return textField
|
||||
}()
|
||||
|
||||
|
@ -174,6 +204,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
|
||||
let passwordTextField: UITextField = {
|
||||
let textField = UITextField()
|
||||
textField.returnKeyType = .next // set to "Return" depends on if the last input field or not
|
||||
textField.autocapitalizationType = .none
|
||||
textField.autocorrectionType = .no
|
||||
textField.keyboardType = .asciiCapable
|
||||
|
@ -187,6 +218,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||
textField.leftView = paddingView
|
||||
textField.leftViewMode = .always
|
||||
textField.font = MastodonRegisterViewController.textFieldLabelFont
|
||||
return textField
|
||||
}()
|
||||
|
||||
|
@ -206,6 +238,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
|
||||
lazy var reasonTextField: UITextField = {
|
||||
let textField = UITextField()
|
||||
textField.returnKeyType = .next // set to "Return" depends on if the last input field or not
|
||||
textField.autocapitalizationType = .none
|
||||
textField.autocorrectionType = .no
|
||||
textField.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color
|
||||
|
@ -217,6 +250,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
|||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||
textField.leftView = paddingView
|
||||
textField.leftViewMode = .always
|
||||
textField.font = MastodonRegisterViewController.textFieldLabelFont
|
||||
return textField
|
||||
}()
|
||||
|
||||
|
@ -361,14 +395,13 @@ extension MastodonRegisterViewController {
|
|||
stackView.setCustomSpacing(6, after: passwordTextField)
|
||||
stackView.setCustomSpacing(32, after: passwordCheckLabel)
|
||||
|
||||
//return
|
||||
// return
|
||||
if viewModel.approvalRequired {
|
||||
passwordTextField.returnKeyType = .continue
|
||||
reasonTextField.returnKeyType = .done
|
||||
} else {
|
||||
passwordTextField.returnKeyType = .done
|
||||
}
|
||||
reasonTextField.returnKeyType = .done
|
||||
|
||||
|
||||
// button
|
||||
stackView.addArrangedSubview(buttonContainer)
|
||||
signUpButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -626,6 +659,25 @@ extension MastodonRegisterViewController: UITextFieldDelegate {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
let text = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
|
||||
switch textField {
|
||||
case usernameTextField:
|
||||
viewModel.username.value = text
|
||||
case displayNameTextField:
|
||||
viewModel.displayName.value = text
|
||||
case emailTextField:
|
||||
viewModel.email.value = text
|
||||
case passwordTextField:
|
||||
viewModel.password.value = text
|
||||
case reasonTextField:
|
||||
viewModel.reason.value = text
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
switch textField {
|
||||
|
|
|
@ -106,6 +106,7 @@ final class MastodonRegisterViewModel {
|
|||
case .success:
|
||||
let text = L10n.Scene.Register.Error.Reason.taken(L10n.Scene.Register.Error.Item.username)
|
||||
self.usernameErrorPrompt.value = MastodonRegisterViewModel.errorPromptAttributedString(for: text)
|
||||
self.usernameValidateState.value = .invalid
|
||||
case .failure:
|
||||
break
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
|
|||
|
||||
let rulesLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .preferredFont(forTextStyle: .body)
|
||||
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))
|
||||
label.textColor = Asset.Colors.Label.primary.color
|
||||
label.text = "Rules"
|
||||
label.numberOfLines = 0
|
||||
|
@ -66,8 +66,6 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
|
|||
|
||||
let confirmButton: PrimaryActionButton = {
|
||||
let button = PrimaryActionButton()
|
||||
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
|
||||
button.setTitleColor(.white, for: .normal)
|
||||
button.setTitle(L10n.Scene.ServerRules.Button.confirm, for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
|
|
@ -10,14 +10,13 @@ import Combine
|
|||
import MastodonSDK
|
||||
|
||||
final class MastodonServerRulesViewModel {
|
||||
|
||||
// input
|
||||
|
||||
let domain: String
|
||||
let authenticateInfo: AuthenticationViewModel.AuthenticateInfo
|
||||
let rules: [Mastodon.Entity.Instance.Rule]
|
||||
let instance: Mastodon.Entity.Instance
|
||||
let applicationToken: Mastodon.Entity.Token
|
||||
|
||||
|
||||
init(
|
||||
domain: String,
|
||||
|
@ -36,14 +35,19 @@ final class MastodonServerRulesViewModel {
|
|||
var rulesAttributedString: NSAttributedString {
|
||||
let attributedString = NSMutableAttributedString(string: "\n")
|
||||
let configuration = UIImage.SymbolConfiguration(font: .preferredFont(forTextStyle: .title3))
|
||||
let separatorString = Array(repeating: " ", count: 4).joined()
|
||||
for (i, rule) in rules.enumerated() {
|
||||
guard i < 50 else {
|
||||
return NSAttributedString(string: "\(i)" + separatorString + rule.text + "\n\n")
|
||||
}
|
||||
let imageName = String(i + 1) + ".circle.fill"
|
||||
let image = UIImage(systemName: imageName, withConfiguration: configuration)!
|
||||
let attachment = NSTextAttachment()
|
||||
attachment.image = image.withTintColor(Asset.Colors.Label.primary.color)
|
||||
let imageAttribute = NSAttributedString(attachment: attachment)
|
||||
|
||||
let ruleString = NSAttributedString(string: " " + rule.text + "\n\n")
|
||||
let imageAttribute = NSMutableAttributedString(attachment: attachment)
|
||||
imageAttribute.addAttributes([NSAttributedString.Key.baselineOffset : -1.5], range: NSRange(location: 0, length: imageAttribute.length))
|
||||
|
||||
let ruleString = NSAttributedString(string: separatorString + rule.text + "\n\n")
|
||||
attributedString.append(imageAttribute)
|
||||
attributedString.append(ruleString)
|
||||
}
|
||||
|
|
|
@ -37,9 +37,10 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
|
|||
private(set) lazy var signUpButton: PrimaryActionButton = {
|
||||
let button = PrimaryActionButton()
|
||||
button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal)
|
||||
let backgroundImageColor: UIColor = traitCollection.userInterfaceIdiom == .phone ? .white : Asset.Colors.Button.normal.color
|
||||
let backgroundImageColor: UIColor = traitCollection.userInterfaceIdiom == .phone ? .white : Asset.Colors.brandBlue.color
|
||||
let backgroundImageHighlightedColor: UIColor = traitCollection.userInterfaceIdiom == .phone ? UIColor(white: 0.8, alpha: 1.0) : Asset.Colors.brandBlueDarken20.color
|
||||
button.setBackgroundImage(.placeholder(color: backgroundImageColor), for: .normal)
|
||||
button.setBackgroundImage(.placeholder(color: backgroundImageColor.withAlphaComponent(0.9)), for: .highlighted)
|
||||
button.setBackgroundImage(.placeholder(color: backgroundImageHighlightedColor), for: .highlighted)
|
||||
let titleColor: UIColor = traitCollection.userInterfaceIdiom == .phone ? Asset.Colors.Button.normal.color : UIColor.white
|
||||
button.setTitleColor(titleColor, for: .normal)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -67,6 +68,8 @@ extension WelcomeViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.overrideUserInterfaceStyle = .light
|
||||
|
||||
setupOnboardingAppearance()
|
||||
setupIllustrationLayout()
|
||||
|
||||
|
|
|
@ -39,7 +39,8 @@ extension PrimaryActionButton {
|
|||
titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))
|
||||
setTitleColor(.white, for: .normal)
|
||||
setBackgroundImage(UIImage.placeholder(color: Asset.Colors.brandBlue.color), for: .normal)
|
||||
setupButtonBackground()
|
||||
setBackgroundImage(UIImage.placeholder(color: Asset.Colors.brandBlueDarken20.color), for: .highlighted)
|
||||
setBackgroundImage(UIImage.placeholder(color: Asset.Colors.disabled.color), for: .disabled)
|
||||
applyCornerRadius(radius: 10)
|
||||
}
|
||||
|
||||
|
@ -68,20 +69,4 @@ extension PrimaryActionButton {
|
|||
self.setTitle(originalButtonTitle, for: .disabled)
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
setupButtonBackground()
|
||||
}
|
||||
|
||||
func setupButtonBackground() {
|
||||
if UIScreen.main.traitCollection.userInterfaceStyle == .light {
|
||||
setTitleColor(.white, for: .disabled)
|
||||
setBackgroundImage(UIImage.placeholder(color: Asset.Colors.Button.normal.color.withAlphaComponent(0.5)), for: .highlighted)
|
||||
setBackgroundImage(UIImage.placeholder(color: Asset.Colors.Button.disabled.color), for: .disabled)
|
||||
|
||||
} else {
|
||||
setTitleColor(UIColor.white.withAlphaComponent(0.5), for: .disabled)
|
||||
setBackgroundImage(UIImage.placeholder(color: Asset.Colors.brandBlue.color.withAlphaComponent(0.5)), for: .highlighted)
|
||||
setBackgroundImage(UIImage.placeholder(color: Asset.Colors.brandBlue.color.withAlphaComponent(0.5)), for: .disabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue