diff --git a/Mastodon/Diffiable/Section/PickServerSection.swift b/Mastodon/Diffiable/Section/PickServerSection.swift index 182c93924..28b1ded3f 100644 --- a/Mastodon/Diffiable/Section/PickServerSection.swift +++ b/Mastodon/Diffiable/Section/PickServerSection.swift @@ -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() } } ) diff --git a/Mastodon/Generated/Assets.swift b/Mastodon/Generated/Assets.swift index 043360043..1ea9e8d6a 100644 --- a/Mastodon/Generated/Assets.swift +++ b/Mastodon/Generated/Assets.swift @@ -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") diff --git a/Mastodon/Resources/Assets.xcassets/Colors/brand.blue.darken.20.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/brand.blue.darken.20.colorset/Contents.json new file mode 100644 index 000000000..6464e2d9d --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Colors/brand.blue.darken.20.colorset/Contents.json @@ -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 + } +} diff --git a/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift index 54994cb1f..d5fb66e39 100644 --- a/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift +++ b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift @@ -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 }() diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift index 2a978c691..46c5e234d 100644 --- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift @@ -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 diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+LoadIndexedServerState.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+LoadIndexedServerState.swift index 69f6a82fb..0c4910d17 100644 --- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+LoadIndexedServerState.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+LoadIndexedServerState.swift @@ -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 } diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift index 8348f8843..95fc2be04 100644 --- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift @@ -61,6 +61,7 @@ class MastodonPickServerViewModel: NSObject { let error = CurrentValueSubject(nil) let isLoadingIndexedServers = CurrentValueSubject(false) + let loadingIndexedServersError = CurrentValueSubject(nil) let emptyStateViewState = CurrentValueSubject(.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(), diff --git a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift index 8eb0cb771..ca69aef29 100644 --- a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift @@ -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) diff --git a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift index 5d10a39b5..61330452a 100644 --- a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift @@ -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 diff --git a/Mastodon/Scene/Onboarding/PickServer/View/PickServerEmptyStateView.swift b/Mastodon/Scene/Onboarding/PickServer/View/PickServerEmptyStateView.swift index af744fa92..6a30751a9 100644 --- a/Mastodon/Scene/Onboarding/PickServer/View/PickServerEmptyStateView.swift +++ b/Mastodon/Scene/Onboarding/PickServer/View/PickServerEmptyStateView.swift @@ -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…") } } } diff --git a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift index b6214d4e7..ebd9b6ead 100644 --- a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift +++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift @@ -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 { diff --git a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift index 85b934a25..6c9e07542 100644 --- a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift +++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift @@ -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 } diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift index d865c96ec..8b238cf0f 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift @@ -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 }() diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift index b1d000db5..c6a37e859 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift @@ -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) } diff --git a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift index 3654c9f08..dea4de017 100644 --- a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift +++ b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift @@ -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() diff --git a/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift b/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift index 8fefc06c8..82d0319f7 100644 --- a/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift +++ b/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift @@ -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) - } - } }