mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-22 08:10:59 +00:00
Sign in overhaul
This commit is contained in:
parent
9577d27a92
commit
a2dc370a22
9 changed files with 325 additions and 67 deletions
|
@ -30,8 +30,11 @@
|
||||||
"add-identity.browse" = "Browse";
|
"add-identity.browse" = "Browse";
|
||||||
"add-identity.instance-not-supported" = "In order to provide a safe experience to all users and comply with the App Store Review Guidelines, this instance is not supported.";
|
"add-identity.instance-not-supported" = "In order to provide a safe experience to all users and comply with the App Store Review Guidelines, this instance is not supported.";
|
||||||
"add-identity.join" = "Join";
|
"add-identity.join" = "Join";
|
||||||
|
"add-identity.prompt" = "Enter the URL of the Mastodon instance you wish to connect to:";
|
||||||
"add-identity.request-invite" = "Request an invite";
|
"add-identity.request-invite" = "Request an invite";
|
||||||
"add-identity.unable-to-connect-to-instance" = "Unable to connect to instance";
|
"add-identity.unable-to-connect-to-instance" = "Unable to connect to instance";
|
||||||
|
"add-identity.welcome" = "Welcome to Metatext";
|
||||||
|
"add-identity.what-is-mastodon" = "What is Mastodon?";
|
||||||
"attachment.edit.description" = "Describe for the visually impaired";
|
"attachment.edit.description" = "Describe for the visually impaired";
|
||||||
"attachment.edit.description.audio" = "Describe for people with hearing loss";
|
"attachment.edit.description.audio" = "Describe for people with hearing loss";
|
||||||
"attachment.edit.description.video" = "Describe for people with hearing loss or visual impairment";
|
"attachment.edit.description.video" = "Describe for people with hearing loss or visual impairment";
|
||||||
|
|
|
@ -154,6 +154,7 @@
|
||||||
D0F0B12E251A97E400942152 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0B12D251A97E400942152 /* TableViewController.swift */; };
|
D0F0B12E251A97E400942152 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0B12D251A97E400942152 /* TableViewController.swift */; };
|
||||||
D0F0B136251AA12700942152 /* CollectionItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0B135251AA12700942152 /* CollectionItem+Extensions.swift */; };
|
D0F0B136251AA12700942152 /* CollectionItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0B135251AA12700942152 /* CollectionItem+Extensions.swift */; };
|
||||||
D0F2D54B2581CF7D00986197 /* VisualEffectBlur.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F2D54A2581CF7D00986197 /* VisualEffectBlur.swift */; };
|
D0F2D54B2581CF7D00986197 /* VisualEffectBlur.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F2D54A2581CF7D00986197 /* VisualEffectBlur.swift */; };
|
||||||
|
D0F4362D25C10B9600E4F896 /* AddIdentityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F4362C25C10B9600E4F896 /* AddIdentityViewController.swift */; };
|
||||||
D0F5880525A7E4C500E3A49C /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = D0F5880425A7E4C500E3A49C /* Kingfisher */; };
|
D0F5880525A7E4C500E3A49C /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = D0F5880425A7E4C500E3A49C /* Kingfisher */; };
|
||||||
D0F5880F25A7E6CC00E3A49C /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = D0F5880E25A7E6CC00E3A49C /* Kingfisher */; };
|
D0F5880F25A7E6CC00E3A49C /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = D0F5880E25A7E6CC00E3A49C /* Kingfisher */; };
|
||||||
D0FCC105259C4E61000B67DF /* NewStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FCC104259C4E61000B67DF /* NewStatusViewController.swift */; };
|
D0FCC105259C4E61000B67DF /* NewStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FCC104259C4E61000B67DF /* NewStatusViewController.swift */; };
|
||||||
|
@ -336,6 +337,7 @@
|
||||||
D0F0B12D251A97E400942152 /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = "<group>"; };
|
D0F0B12D251A97E400942152 /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = "<group>"; };
|
||||||
D0F0B135251AA12700942152 /* CollectionItem+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CollectionItem+Extensions.swift"; sourceTree = "<group>"; };
|
D0F0B135251AA12700942152 /* CollectionItem+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CollectionItem+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D0F2D54A2581CF7D00986197 /* VisualEffectBlur.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffectBlur.swift; sourceTree = "<group>"; };
|
D0F2D54A2581CF7D00986197 /* VisualEffectBlur.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffectBlur.swift; sourceTree = "<group>"; };
|
||||||
|
D0F4362C25C10B9600E4F896 /* AddIdentityViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIdentityViewController.swift; sourceTree = "<group>"; };
|
||||||
D0FCC104259C4E61000B67DF /* NewStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStatusViewController.swift; sourceTree = "<group>"; };
|
D0FCC104259C4E61000B67DF /* NewStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStatusViewController.swift; sourceTree = "<group>"; };
|
||||||
D0FE1C8E253686F9003EF1EB /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = "<group>"; };
|
D0FE1C8E253686F9003EF1EB /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = "<group>"; };
|
||||||
D0FE1C9725368A9D003EF1EB /* PlayerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerCache.swift; sourceTree = "<group>"; };
|
D0FE1C9725368A9D003EF1EB /* PlayerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerCache.swift; sourceTree = "<group>"; };
|
||||||
|
@ -564,6 +566,7 @@
|
||||||
D0C7D43024F76169001EBDBB /* View Controllers */ = {
|
D0C7D43024F76169001EBDBB /* View Controllers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D0F4362C25C10B9600E4F896 /* AddIdentityViewController.swift */,
|
||||||
D05936CE25A8D79800754FDF /* EditAttachmentViewController.swift */,
|
D05936CE25A8D79800754FDF /* EditAttachmentViewController.swift */,
|
||||||
D088406C25AFBBE200BB749B /* EmojiPickerViewController.swift */,
|
D088406C25AFBBE200BB749B /* EmojiPickerViewController.swift */,
|
||||||
D087671525BAA8C0001FDD43 /* ExploreViewController.swift */,
|
D087671525BAA8C0001FDD43 /* ExploreViewController.swift */,
|
||||||
|
@ -879,6 +882,7 @@
|
||||||
D0C7D4D624F7616A001EBDBB /* NSMutableAttributedString+Extensions.swift in Sources */,
|
D0C7D4D624F7616A001EBDBB /* NSMutableAttributedString+Extensions.swift in Sources */,
|
||||||
D0E9F9AA258450B300EF503D /* CompositionInputAccessoryView.swift in Sources */,
|
D0E9F9AA258450B300EF503D /* CompositionInputAccessoryView.swift in Sources */,
|
||||||
D0849C7F25903C4900A5EBCC /* Status+Extensions.swift in Sources */,
|
D0849C7F25903C4900A5EBCC /* Status+Extensions.swift in Sources */,
|
||||||
|
D0F4362D25C10B9600E4F896 /* AddIdentityViewController.swift in Sources */,
|
||||||
D0625E59250F092900502611 /* StatusListCell.swift in Sources */,
|
D0625E59250F092900502611 /* StatusListCell.swift in Sources */,
|
||||||
D0E569DB2529319100FA1D72 /* LoadMoreView.swift in Sources */,
|
D0E569DB2529319100FA1D72 /* LoadMoreView.swift in Sources */,
|
||||||
D05936FF25AA94EA00754FDF /* MarkAttachmentsSensitiveView.swift in Sources */,
|
D05936FF25AA94EA00754FDF /* MarkAttachmentsSensitiveView.swift in Sources */,
|
||||||
|
|
298
View Controllers/AddIdentityViewController.swift
Normal file
298
View Controllers/AddIdentityViewController.swift
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
// Copyright © 2021 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Kingfisher
|
||||||
|
import Mastodon
|
||||||
|
import SafariServices
|
||||||
|
import SwiftUI
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
final class AddIdentityViewController: UIViewController {
|
||||||
|
private let viewModel: AddIdentityViewModel
|
||||||
|
private let displayWelcome: Bool
|
||||||
|
private let scrollView = UIScrollView()
|
||||||
|
private let stackView = UIStackView()
|
||||||
|
private let promptLabel = UILabel()
|
||||||
|
private let urlTextField = UITextField()
|
||||||
|
private let welcomeLabel = UILabel()
|
||||||
|
private let instanceVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterial))
|
||||||
|
private let instanceStackView = UIStackView()
|
||||||
|
private let instanceTitleLabel = UILabel()
|
||||||
|
private let instanceURLLabel = UILabel()
|
||||||
|
private let instanceImageView = AnimatedImageView()
|
||||||
|
private let logInButton = UIButton(type: .system)
|
||||||
|
private let activityIndicator = UIActivityIndicatorView()
|
||||||
|
private let joinButton = UIButton(type: .system)
|
||||||
|
private let browseButton = UIButton(type: .system)
|
||||||
|
private let whatIsMastodonButton = UIButton(type: .system)
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
init(viewModel: AddIdentityViewModel, displayWelcome: Bool) {
|
||||||
|
self.viewModel = viewModel
|
||||||
|
self.displayWelcome = displayWelcome
|
||||||
|
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
configureViews()
|
||||||
|
setupViewHierarchy()
|
||||||
|
setupConstraints()
|
||||||
|
setupViewModelBindings()
|
||||||
|
initialDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(true)
|
||||||
|
|
||||||
|
viewModel.refreshFilter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension AddIdentityViewController {
|
||||||
|
static let whatIsMastodonURL = URL(string: "https://joinmastodon.org")!
|
||||||
|
|
||||||
|
// swiftlint:disable:next function_body_length
|
||||||
|
func configureViews() {
|
||||||
|
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
|
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
stackView.spacing = 20
|
||||||
|
stackView.axis = .vertical
|
||||||
|
stackView.distribution = .equalSpacing
|
||||||
|
|
||||||
|
welcomeLabel.numberOfLines = 0
|
||||||
|
welcomeLabel.textAlignment = .center
|
||||||
|
welcomeLabel.adjustsFontForContentSizeCategory = true
|
||||||
|
welcomeLabel.font = .preferredFont(forTextStyle: .largeTitle)
|
||||||
|
welcomeLabel.text = NSLocalizedString("add-identity.welcome", comment: "")
|
||||||
|
|
||||||
|
promptLabel.numberOfLines = 0
|
||||||
|
promptLabel.textAlignment = .center
|
||||||
|
promptLabel.adjustsFontForContentSizeCategory = true
|
||||||
|
promptLabel.font = .preferredFont(forTextStyle: .callout)
|
||||||
|
promptLabel.text = NSLocalizedString("add-identity.prompt", comment: "")
|
||||||
|
|
||||||
|
urlTextField.borderStyle = .roundedRect
|
||||||
|
urlTextField.textContentType = .URL
|
||||||
|
urlTextField.autocapitalizationType = .none
|
||||||
|
urlTextField.autocorrectionType = .no
|
||||||
|
urlTextField.keyboardType = .URL
|
||||||
|
urlTextField.placeholder = NSLocalizedString("add-identity.instance-url", comment: "")
|
||||||
|
urlTextField.addAction(
|
||||||
|
UIAction { [weak self] _ in self?.viewModel.urlFieldText = self?.urlTextField.text ?? "" },
|
||||||
|
for: .editingChanged)
|
||||||
|
|
||||||
|
logInButton.setTitle(NSLocalizedString("add-identity.log-in", comment: ""), for: .normal)
|
||||||
|
logInButton.addAction(
|
||||||
|
UIAction { [weak self] _ in self?.viewModel.logInTapped() },
|
||||||
|
for: .touchUpInside)
|
||||||
|
|
||||||
|
activityIndicator.hidesWhenStopped = true
|
||||||
|
|
||||||
|
instanceVisualEffectView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
|
instanceStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
instanceStackView.axis = .vertical
|
||||||
|
instanceStackView.spacing = .compactSpacing
|
||||||
|
|
||||||
|
instanceTitleLabel.numberOfLines = 0
|
||||||
|
instanceTitleLabel.textAlignment = .center
|
||||||
|
instanceTitleLabel.adjustsFontForContentSizeCategory = true
|
||||||
|
instanceTitleLabel.font = .preferredFont(forTextStyle: .headline)
|
||||||
|
|
||||||
|
instanceURLLabel.numberOfLines = 0
|
||||||
|
instanceURLLabel.textAlignment = .center
|
||||||
|
instanceURLLabel.adjustsFontForContentSizeCategory = true
|
||||||
|
instanceURLLabel.font = .preferredFont(forTextStyle: .subheadline)
|
||||||
|
instanceURLLabel.textColor = .secondaryLabel
|
||||||
|
|
||||||
|
instanceImageView.contentMode = .scaleAspectFill
|
||||||
|
instanceImageView.layer.cornerRadius = .defaultCornerRadius
|
||||||
|
instanceImageView.clipsToBounds = true
|
||||||
|
instanceImageView.kf.indicatorType = .activity
|
||||||
|
instanceImageView.isHidden = true
|
||||||
|
|
||||||
|
joinButton.addAction(UIAction { [weak self] _ in self?.join() }, for: .touchUpInside)
|
||||||
|
|
||||||
|
browseButton.setTitle(NSLocalizedString("add-identity.browse", comment: ""), for: .normal)
|
||||||
|
browseButton.isHidden = true
|
||||||
|
browseButton.addAction(
|
||||||
|
UIAction { [weak self] _ in self?.viewModel.browseTapped() },
|
||||||
|
for: .touchUpInside)
|
||||||
|
|
||||||
|
whatIsMastodonButton.setTitle(NSLocalizedString("add-identity.what-is-mastodon", comment: ""), for: .normal)
|
||||||
|
whatIsMastodonButton.addAction(
|
||||||
|
UIAction { [weak self] _ in
|
||||||
|
self?.present(SFSafariViewController(url: Self.whatIsMastodonURL), animated: true)
|
||||||
|
},
|
||||||
|
for: .touchUpInside)
|
||||||
|
|
||||||
|
for button in [logInButton, browseButton, joinButton, whatIsMastodonButton] {
|
||||||
|
button.titleLabel?.adjustsFontForContentSizeCategory = true
|
||||||
|
button.titleLabel?.font = .preferredFont(forTextStyle: .title3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupViewHierarchy() {
|
||||||
|
view.addSubview(scrollView)
|
||||||
|
scrollView.addSubview(stackView)
|
||||||
|
stackView.addArrangedSubview(promptLabel)
|
||||||
|
stackView.addArrangedSubview(urlTextField)
|
||||||
|
stackView.addArrangedSubview(welcomeLabel)
|
||||||
|
instanceStackView.addArrangedSubview(instanceTitleLabel)
|
||||||
|
instanceStackView.addArrangedSubview(instanceURLLabel)
|
||||||
|
instanceVisualEffectView.contentView.addSubview(instanceStackView)
|
||||||
|
instanceImageView.addSubview(instanceVisualEffectView)
|
||||||
|
stackView.addArrangedSubview(instanceImageView)
|
||||||
|
stackView.addArrangedSubview(activityIndicator)
|
||||||
|
stackView.addArrangedSubview(logInButton)
|
||||||
|
stackView.addArrangedSubview(joinButton)
|
||||||
|
stackView.addArrangedSubview(browseButton)
|
||||||
|
stackView.addArrangedSubview(whatIsMastodonButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupConstraints() {
|
||||||
|
let instanceImageViewWidthConstraint = instanceImageView.widthAnchor.constraint(
|
||||||
|
equalTo: instanceImageView.heightAnchor, multiplier: 16 / 9)
|
||||||
|
instanceImageViewWidthConstraint.priority = .justBelowMax
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||||
|
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||||
|
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||||
|
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||||
|
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: .defaultSpacing),
|
||||||
|
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
|
||||||
|
stackView.widthAnchor.constraint(equalTo: scrollView.readableContentGuide.widthAnchor),
|
||||||
|
stackView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
|
||||||
|
instanceImageViewWidthConstraint,
|
||||||
|
instanceVisualEffectView.leadingAnchor.constraint(equalTo: instanceImageView.leadingAnchor),
|
||||||
|
instanceVisualEffectView.trailingAnchor.constraint(equalTo: instanceImageView.trailingAnchor),
|
||||||
|
instanceVisualEffectView.bottomAnchor.constraint(equalTo: instanceImageView.bottomAnchor),
|
||||||
|
instanceStackView.leadingAnchor.constraint(equalTo: instanceVisualEffectView.contentView.leadingAnchor),
|
||||||
|
instanceStackView.topAnchor.constraint(equalTo: instanceVisualEffectView.contentView.topAnchor,
|
||||||
|
constant: .defaultSpacing),
|
||||||
|
instanceStackView.trailingAnchor.constraint(equalTo: instanceVisualEffectView.contentView.trailingAnchor),
|
||||||
|
instanceStackView.bottomAnchor.constraint(equalTo: instanceVisualEffectView.contentView.bottomAnchor,
|
||||||
|
constant: -.defaultSpacing)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupViewModelBindings() {
|
||||||
|
viewModel.$loading.sink { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
if $0 {
|
||||||
|
self.activityIndicator.startAnimating()
|
||||||
|
self.logInButton.isHidden = true
|
||||||
|
self.joinButton.isHidden = true
|
||||||
|
self.browseButton.isHidden = true
|
||||||
|
self.whatIsMastodonButton.isHidden = true
|
||||||
|
} else {
|
||||||
|
self.activityIndicator.stopAnimating()
|
||||||
|
self.logInButton.isHidden = false
|
||||||
|
self.joinButton.isHidden = !(self.viewModel.instance?.registrations ?? true)
|
||||||
|
self.browseButton.isHidden = !self.viewModel.isPublicTimelineAvailable
|
||||||
|
self.whatIsMastodonButton.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
viewModel.$instance.combineLatest(viewModel.$isPublicTimelineAvailable)
|
||||||
|
.sink { [weak self] in self?.configure(instance: $0, isPublicTimelineAvailable: $1) }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
viewModel.$alertItem
|
||||||
|
.compactMap { $0 }
|
||||||
|
.sink { [weak self] in self?.present(alertItem: $0) }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initialDisplay() {
|
||||||
|
if displayWelcome {
|
||||||
|
welcomeLabel.alpha = 0
|
||||||
|
promptLabel.alpha = 0
|
||||||
|
urlTextField.alpha = 0
|
||||||
|
logInButton.alpha = 0
|
||||||
|
whatIsMastodonButton.alpha = 0
|
||||||
|
|
||||||
|
UIView.animate(withDuration: .longAnimationDuration * 2) {
|
||||||
|
self.welcomeLabel.alpha = 1
|
||||||
|
} completion: { _ in
|
||||||
|
UIView.animate(withDuration: .longAnimationDuration * 2) {
|
||||||
|
self.welcomeLabel.alpha = 0
|
||||||
|
} completion: { _ in
|
||||||
|
self.welcomeLabel.isHidden = true
|
||||||
|
UIView.animate(withDuration: .longAnimationDuration) {
|
||||||
|
self.promptLabel.alpha = 1
|
||||||
|
} completion: { _ in
|
||||||
|
UIView.animate(withDuration: .longAnimationDuration) {
|
||||||
|
self.urlTextField.alpha = 1
|
||||||
|
} completion: { _ in
|
||||||
|
self.urlTextField.becomeFirstResponder()
|
||||||
|
UIView.animate(withDuration: .longAnimationDuration) {
|
||||||
|
self.logInButton.alpha = 1
|
||||||
|
} completion: { _ in
|
||||||
|
UIView.animate(withDuration: .longAnimationDuration) {
|
||||||
|
self.whatIsMastodonButton.alpha = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
welcomeLabel.isHidden = true
|
||||||
|
whatIsMastodonButton.isHidden = true
|
||||||
|
urlTextField.becomeFirstResponder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(instance: Instance?, isPublicTimelineAvailable: Bool) {
|
||||||
|
if let instance = instance {
|
||||||
|
instanceTitleLabel.text = instance.title
|
||||||
|
instanceURLLabel.text = instance.uri
|
||||||
|
instanceImageView.kf.setImage(with: instance.thumbnail)
|
||||||
|
instanceImageView.isHidden = false
|
||||||
|
|
||||||
|
if instance.registrations {
|
||||||
|
let joinButtonTitle: String
|
||||||
|
|
||||||
|
if instance.approvalRequired {
|
||||||
|
joinButtonTitle = NSLocalizedString("add-identity.request-invite", comment: "")
|
||||||
|
} else {
|
||||||
|
joinButtonTitle = NSLocalizedString("add-identity.join", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
joinButton.setTitle(joinButtonTitle, for: .normal)
|
||||||
|
joinButton.isHidden = false
|
||||||
|
} else {
|
||||||
|
joinButton.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
browseButton.isHidden = !isPublicTimelineAvailable
|
||||||
|
} else {
|
||||||
|
instanceImageView.isHidden = true
|
||||||
|
joinButton.isHidden = true
|
||||||
|
browseButton.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func join() {
|
||||||
|
guard let instance = viewModel.instance, let url = viewModel.url else { return }
|
||||||
|
|
||||||
|
let registrationViewModel = viewModel.registrationViewModel(instance: instance, url: url)
|
||||||
|
let registrationView = RegistrationView(viewModel: registrationViewModel)
|
||||||
|
let registrationViewController = UIHostingController(rootView: registrationView)
|
||||||
|
|
||||||
|
show(registrationViewController, sender: self)
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ public final class AddIdentityViewModel: ObservableObject {
|
||||||
self.instanceURLService = instanceURLService
|
self.instanceURLService = instanceURLService
|
||||||
|
|
||||||
let url = $urlFieldText
|
let url = $urlFieldText
|
||||||
.debounce(for: .seconds(Self.textFieldDebounceInterval), scheduler: DispatchQueue.global())
|
.throttle(for: .seconds(Self.textFieldThrottleInterval), scheduler: DispatchQueue.global(), latest: true)
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.flatMap {
|
.flatMap {
|
||||||
instanceURLService.url(text: $0).publisher
|
instanceURLService.url(text: $0).publisher
|
||||||
|
@ -86,7 +86,7 @@ public extension AddIdentityViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension AddIdentityViewModel {
|
private extension AddIdentityViewModel {
|
||||||
private static let textFieldDebounceInterval: TimeInterval = 0.5
|
private static let textFieldThrottleInterval: TimeInterval = 0.5
|
||||||
func addIdentity(kind: AllIdentitiesService.IdentityCreation) {
|
func addIdentity(kind: AllIdentitiesService.IdentityCreation) {
|
||||||
instanceURLService.url(text: urlFieldText).publisher
|
instanceURLService.url(text: urlFieldText).publisher
|
||||||
.map { ($0, kind) }
|
.map { ($0, kind) }
|
||||||
|
|
|
@ -4,68 +4,16 @@ import Kingfisher
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import ViewModels
|
import ViewModels
|
||||||
|
|
||||||
struct AddIdentityView: View {
|
struct AddIdentityView: UIViewControllerRepresentable {
|
||||||
@StateObject var viewModel: AddIdentityViewModel
|
let viewModelClosure: () -> AddIdentityViewModel
|
||||||
@Environment(\.accessibilityReduceMotion) var accessibilityReduceMotion
|
let displayWelcome: Bool
|
||||||
@EnvironmentObject var rootViewModel: RootViewModel
|
|
||||||
|
|
||||||
var body: some View {
|
func makeUIViewController(context: Context) -> AddIdentityViewController {
|
||||||
Form {
|
AddIdentityViewController(viewModel: viewModelClosure(), displayWelcome: displayWelcome)
|
||||||
Section {
|
|
||||||
TextField("add-identity.instance-url", text: $viewModel.urlFieldText)
|
|
||||||
.textContentType(.URL)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.keyboardType(.URL)
|
|
||||||
if let instance = viewModel.instance {
|
|
||||||
VStack(alignment: .center) {
|
|
||||||
KFImage(instance.thumbnail)
|
|
||||||
.placeholder {
|
|
||||||
ProgressView()
|
|
||||||
}
|
}
|
||||||
.resizable()
|
|
||||||
.aspectRatio(16 / 9, contentMode: .fill)
|
func updateUIViewController(_ uiViewController: AddIdentityViewController, context: Context) {
|
||||||
.background(Color.blue)
|
|
||||||
Spacer()
|
|
||||||
Text(instance.title)
|
|
||||||
.font(.headline)
|
|
||||||
Text(instance.uri)
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
.listRowInsets(EdgeInsets())
|
|
||||||
}
|
|
||||||
Group {
|
|
||||||
if viewModel.loading {
|
|
||||||
ProgressView()
|
|
||||||
} else {
|
|
||||||
Button("add-identity.log-in",
|
|
||||||
action: viewModel.logInTapped)
|
|
||||||
if viewModel.isPublicTimelineAvailable {
|
|
||||||
Button("add-identity.browse", action: viewModel.browseTapped)
|
|
||||||
}
|
|
||||||
if let instance = viewModel.instance,
|
|
||||||
let url = viewModel.url,
|
|
||||||
instance.registrations {
|
|
||||||
NavigationLink(
|
|
||||||
instance.approvalRequired
|
|
||||||
? "add-identity.request-invite"
|
|
||||||
: "add-identity.join",
|
|
||||||
destination: RegistrationView(
|
|
||||||
viewModel: viewModel.registrationViewModel(
|
|
||||||
instance: instance,
|
|
||||||
url: url)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.animation(.default, if: !accessibilityReduceMotion)
|
|
||||||
.alertItem($viewModel.alertItem)
|
|
||||||
.onAppear(perform: viewModel.refreshFilter)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +35,7 @@ import PreviewViewModels
|
||||||
struct AddAccountView_Previews: PreviewProvider {
|
struct AddAccountView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
AddIdentityView(viewModel: RootViewModel.preview.addIdentityViewModel())
|
AddIdentityView(viewModelClosure: { RootViewModel.preview.addIdentityViewModel() }, displayWelcome: false)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,9 @@ struct IdentitiesView: View {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
NavigationLink(
|
NavigationLink(
|
||||||
destination: AddIdentityView(viewModel: rootViewModel.addIdentityViewModel()),
|
destination: AddIdentityView(
|
||||||
|
viewModelClosure: { rootViewModel.addIdentityViewModel() },
|
||||||
|
displayWelcome: false),
|
||||||
label: {
|
label: {
|
||||||
Label("add", systemImage: "plus.circle")
|
Label("add", systemImage: "plus.circle")
|
||||||
})
|
})
|
||||||
|
|
|
@ -50,7 +50,7 @@ struct RegistrationView: View {
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
}
|
}
|
||||||
.alertItem($viewModel.alertItem)
|
.alertItem($viewModel.alertItem)
|
||||||
.sheet(item: $presentURL) { SafariView(url: $0) }
|
.sheet(item: $presentURL) { SafariView(url: $0).edgesIgnoringSafeArea(.all) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,9 @@ struct RootView: View {
|
||||||
.edgesIgnoringSafeArea(.all)
|
.edgesIgnoringSafeArea(.all)
|
||||||
} else {
|
} else {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
AddIdentityView(viewModel: viewModel.addIdentityViewModel())
|
AddIdentityView(
|
||||||
|
viewModelClosure: { viewModel.addIdentityViewModel() },
|
||||||
|
displayWelcome: true)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ extension CGRect {
|
||||||
extension TimeInterval {
|
extension TimeInterval {
|
||||||
static let defaultAnimationDuration: Self = 0.5
|
static let defaultAnimationDuration: Self = 0.5
|
||||||
static let shortAnimationDuration = defaultAnimationDuration / 2
|
static let shortAnimationDuration = defaultAnimationDuration / 2
|
||||||
|
static let longAnimationDuration: Self = 1
|
||||||
|
|
||||||
static func zeroIfReduceMotion(_ duration: Self) -> Self { UIAccessibility.isReduceMotionEnabled ? 0 : duration }
|
static func zeroIfReduceMotion(_ duration: Self) -> Self { UIAccessibility.isReduceMotionEnabled ? 0 : duration }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue