feat: confirmEmail Page

This commit is contained in:
sunxiaojian 2021-02-23 15:38:05 +08:00
parent ea79ca771d
commit f5621991d8
9 changed files with 269 additions and 1 deletions

View File

@ -36,6 +36,9 @@
2D45E5BF25C9549700A6D639 /* PublicTimelineViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D45E5BE25C9549700A6D639 /* PublicTimelineViewModel+State.swift */; };
2D46975E25C2A54100CF4AA9 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */; };
2D46976425C2A71500CF4AA9 /* UIIamge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D46976325C2A71500CF4AA9 /* UIIamge.swift */; };
2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D59819A25E4A581000FB903 /* MastodonConfirmEmailViewController.swift */; };
2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5981A025E4A593000FB903 /* MastodonConfirmEmailViewModel.swift */; };
2D5981BA25E4D7F8000FB903 /* ThirdPartyMailer in Frameworks */ = {isa = PBXBuildFile; productRef = 2D5981B925E4D7F8000FB903 /* ThirdPartyMailer */; };
2D5A3D0325CF8742002347D6 /* ControlContainableScrollViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */; };
2D5A3D1125CF87AA002347D6 /* AvatarBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D1025CF87AA002347D6 /* AvatarBarButtonItem.swift */; };
2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */; };
@ -218,6 +221,8 @@
2D45E5BE25C9549700A6D639 /* PublicTimelineViewModel+State.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PublicTimelineViewModel+State.swift"; sourceTree = "<group>"; };
2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = "<group>"; };
2D46976325C2A71500CF4AA9 /* UIIamge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIIamge.swift; sourceTree = "<group>"; };
2D59819A25E4A581000FB903 /* MastodonConfirmEmailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonConfirmEmailViewController.swift; sourceTree = "<group>"; };
2D5981A025E4A593000FB903 /* MastodonConfirmEmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonConfirmEmailViewModel.swift; sourceTree = "<group>"; };
2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlContainableScrollViews.swift; sourceTree = "<group>"; };
2D5A3D1025CF87AA002347D6 /* AvatarBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarBarButtonItem.swift; sourceTree = "<group>"; };
2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HomeTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
@ -346,6 +351,7 @@
DB5086B825CC0D6400C2C187 /* Kingfisher in Frameworks */,
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */,
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */,
2D5981BA25E4D7F8000FB903 /* ThirdPartyMailer in Frameworks */,
45B49097460EDE530AD5AA72 /* Pods_Mastodon.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -466,6 +472,15 @@
path = Button;
sourceTree = "<group>";
};
2D59819925E4A55C000FB903 /* ConfirmEmail */ = {
isa = PBXGroup;
children = (
2D59819A25E4A581000FB903 /* MastodonConfirmEmailViewController.swift */,
2D5981A025E4A593000FB903 /* MastodonConfirmEmailViewModel.swift */,
);
path = ConfirmEmail;
sourceTree = "<group>";
};
2D5A3D0125CF8640002347D6 /* Vender */ = {
isa = PBXGroup;
children = (
@ -596,6 +611,7 @@
DB01409B25C40BB600F9F3CF /* Authentication */ = {
isa = PBXGroup;
children = (
2D59819925E4A55C000FB903 /* ConfirmEmail */,
DB0140A625C40C0900F9F3CF /* PinBased */,
DBE0821A25CD382900FD6BBD /* Register */,
DB72602125E36A2500235243 /* ServerRules */,
@ -947,6 +963,7 @@
2D42FF6025C8177C004A627A /* ActiveLabel */,
DB0140BC25C40D7500F9F3CF /* CommonOSLog */,
DB5086B725CC0D6400C2C187 /* Kingfisher */,
2D5981B925E4D7F8000FB903 /* ThirdPartyMailer */,
);
productName = Mastodon;
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
@ -1074,6 +1091,7 @@
2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */,
DB0140BB25C40D7500F9F3CF /* XCRemoteSwiftPackageReference "CommonOSLog" */,
DB5086B625CC0D6400C2C187 /* XCRemoteSwiftPackageReference "Kingfisher" */,
2D5981B825E4D7F8000FB903 /* XCRemoteSwiftPackageReference "ThirdPartyMailer" */,
);
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
projectDirPath = "";
@ -1258,6 +1276,7 @@
files = (
DB98337125C9443200AD9700 /* APIService+Authentication.swift in Sources */,
0FAA0FDF25E0B57E0017CCDE /* WelcomeViewController.swift in Sources */,
2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */,
DB45FB1D25CA9D23005A8AC7 /* APIService+HomeTimeline.swift in Sources */,
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */,
@ -1302,6 +1321,7 @@
2DF75BA125D0E29D00694EC8 /* StatusProvider+TimelinePostTableViewCellDelegate.swift in Sources */,
2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */,
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */,
2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */,
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */,
2D38F1C625CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift in Sources */,
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
@ -1875,6 +1895,14 @@
minimumVersion = 4.0.0;
};
};
2D5981B825E4D7F8000FB903 /* XCRemoteSwiftPackageReference "ThirdPartyMailer" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/vtourraine/ThirdPartyMailer.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.7.1;
};
};
2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/Alamofire/AlamofireNetworkActivityIndicator";
@ -1915,6 +1943,11 @@
package = 2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */;
productName = ActiveLabel;
};
2D5981B925E4D7F8000FB903 /* ThirdPartyMailer */ = {
isa = XCSwiftPackageProductDependency;
package = 2D5981B825E4D7F8000FB903 /* XCRemoteSwiftPackageReference "ThirdPartyMailer" */;
productName = ThirdPartyMailer;
};
2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */ = {
isa = XCSwiftPackageProductDependency;
package = 2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */;

View File

@ -81,6 +81,15 @@
"revision": "2b6054efa051565954e1d2b9da831680026cd768",
"version": "5.0.0"
}
},
{
"package": "ThirdPartyMailer",
"repositoryURL": "https://github.com/vtourraine/ThirdPartyMailer.git",
"state": {
"branch": null,
"revision": "923c60ee7588da47db8cfc4e0f5b96e5e605ef84",
"version": "1.7.1"
}
}
]
},

View File

@ -43,6 +43,7 @@ extension SceneCoordinator {
case mastodonPinBasedAuthentication(viewModel: MastodonPinBasedAuthenticationViewModel)
case mastodonRegister(viewModel: MastodonRegisterViewModel)
case mastodonServerRules(viewModel: MastodonServerRulesViewModel)
case mastodonConfirmEmail(viewModel: MastodonConfirmEmailViewModel)
case alertController(alertController: UIAlertController)
}
@ -151,6 +152,10 @@ private extension SceneCoordinator {
let _viewController = MastodonServerRulesViewController()
_viewController.viewModel = viewModel
viewController = _viewController
case .mastodonConfirmEmail(let viewModel):
let _viewController = MastodonConfirmEmailViewController()
_viewController.viewModel = viewModel
viewController = _viewController
case .alertController(let alertController):
if let popoverPresentationController = alertController.popoverPresentationController {
assert(

View File

@ -68,6 +68,32 @@ internal enum L10n {
}
internal enum Scene {
internal enum ConfirmEmail {
/// We just sent an email to %@,\ntap the link to confirm your account.
internal static func subtitle(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.ConfirmEmail.Subtitle", String(describing: p1))
}
/// One last thing.
internal static let title = L10n.tr("Localizable", "Scene.ConfirmEmail.Title")
internal enum Button {
/// I never got an email
internal static let dontReceiveEmail = L10n.tr("Localizable", "Scene.ConfirmEmail.Button.DontReceiveEmail")
/// Open email app
internal static let openEmailApp = L10n.tr("Localizable", "Scene.ConfirmEmail.Button.OpenEmailApp")
}
internal enum DontReceiveEmail {
/// Give our servers another n seconds before sending another email.\n\nCheck if your email address is correct as well as your junk folder if you havent.
internal static let alertDescription = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.alertDescription")
/// Its too soon to tell.
internal static let alertTitle = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.alertTitle")
}
internal enum OpenEmailApp {
/// We just sent you another email. Check your junk folder if you havent.
internal static let alertDescription = L10n.tr("Localizable", "Scene.ConfirmEmail.OpenEmailApp.alertDescription")
/// Check your inbox.
internal static let alertTitle = L10n.tr("Localizable", "Scene.ConfirmEmail.OpenEmailApp.alertTitle")
}
}
internal enum HomeTimeline {
/// Home
internal static let title = L10n.tr("Localizable", "Scene.HomeTimeline.Title")

View File

@ -66,5 +66,16 @@
<string>Dark</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>sparrow</string>
<string>googlegmail</string>
<string>x-dispatch</string>
<string>readdle-spark</string>
<string>airmail</string>
<string>ms-outlook</string>
<string>ymail</string>
<string>fastmail</string>
</array>
</dict>
</plist>

View File

@ -29,6 +29,7 @@
"Scene.Register.Input.Username.DuplicatePrompt" = "This username is taken.";
"Scene.Register.Input.Username.Placeholder" = "username";
"Scene.Register.Title" = "Tell us about you.";
"Scene.ServerPicker.Input.Placeholder" = "Find a server or join your own...";
"Scene.ServerPicker.Title" = "Pick a Server,
any server.";
@ -36,5 +37,22 @@ any server.";
"Scene.ServerRules.Prompt" = "By continuing, you're subject to the terms of service and privacy policy for %@.";
"Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@.";
"Scene.ServerRules.Title" = "Some ground rules.";
"Scene.Welcome.Slogan" = "Social networking
back in your hands.";
"Scene.ConfirmEmail.Title" = "One last thing.";
"Scene.ConfirmEmail.Subtitle" = "We just sent an email to %@,
tap the link to confirm your account.";
"Scene.ConfirmEmail.Button.OpenEmailApp" = "Open email app";
"Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email";
"Scene.ConfirmEmail.DontReceiveEmail.alertTitle" = "Its too soon to tell.";
"Scene.ConfirmEmail.DontReceiveEmail.alertDescription" = "Give our servers another n seconds before sending another email.
Check if your email address is correct as well as your junk folder if you havent.";
"Scene.ConfirmEmail.OpenEmailApp.alertTitle" = "Check your inbox.";
"Scene.ConfirmEmail.OpenEmailApp.alertDescription" = "We just sent you another email. Check your junk folder if you havent.";

View File

@ -0,0 +1,144 @@
//
// MastodonConfirmEmailViewController.swift
// Mastodon
//
// Created by sxiaojian on 2021/2/23.
//
import Combine
import ThirdPartyMailer
import UIKit
final class MastodonConfirmEmailViewController: UIViewController, NeedsDependency {
var disposeBag = Set<AnyCancellable>()
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var viewModel: MastodonConfirmEmailViewModel!
let largeTitleLabel: UILabel = {
let label = UILabel()
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.boldSystemFont(ofSize: 34))
label.textColor = .label
label.text = L10n.Scene.ConfirmEmail.title
return label
}()
private(set) lazy var subtitleLabel: UILabel = {
let label = UILabel()
label.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: UIFont.systemFont(ofSize: 20))
label.textColor = .secondaryLabel
label.text = L10n.Scene.ConfirmEmail.subtitle(viewModel.email)
label.numberOfLines = 0
return label
}()
let openEmailButton: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
button.setBackgroundImage(UIImage.placeholder(color: Asset.Colors.lightBrandBlue.color), for: .normal)
button.setTitleColor(.white, for: .normal)
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
}()
let dontReceiveButton: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: UIFont.boldSystemFont(ofSize: 15))
button.setTitleColor(Asset.Colors.lightBrandBlue.color, for: .normal)
button.setTitle(L10n.Scene.ConfirmEmail.Button.dontReceiveEmail, for: .normal)
button.addTarget(self, action: #selector(dontReceiveButtonPressed(_:)), for: UIControl.Event.touchUpInside)
return button
}()
}
extension MastodonConfirmEmailViewController {
override func viewDidLoad() {
overrideUserInterfaceStyle = .light
view.backgroundColor = Asset.Colors.Background.onboardingBackground.color
// set navigationBar transparent
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarPosition.any, barMetrics: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.topItem?.title = "Back"
// resizedView
let resizedView = UIView()
resizedView.translatesAutoresizingMaskIntoConstraints = false
resizedView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical)
// stackView
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
stackView.layoutMargins = UIEdgeInsets(top: 10, left: 0, bottom: 23, right: 0)
stackView.isLayoutMarginsRelativeArrangement = true
stackView.addArrangedSubview(self.largeTitleLabel)
stackView.addArrangedSubview(self.subtitleLabel)
stackView.addArrangedSubview(resizedView)
stackView.addArrangedSubview(self.openEmailButton)
stackView.addArrangedSubview(self.dontReceiveButton)
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor),
stackView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
stackView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor),
])
NSLayoutConstraint.activate([
self.openEmailButton.heightAnchor.constraint(equalToConstant: 46),
])
}
}
extension MastodonConfirmEmailViewController {
@objc private func openEmailButtonPressed(_ sender: UIButton) {
let alertController = UIAlertController(title: L10n.Scene.ConfirmEmail.OpenEmailApp.alertTitle, message: L10n.Scene.ConfirmEmail.OpenEmailApp.alertDescription, preferredStyle: .alert)
let openEmailAction = UIAlertAction(title: L10n.Scene.ConfirmEmail.Button.openEmailApp, style: .default) { [weak self] _ in
guard let self = self else { return }
self.showEmailAppAlert()
}
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
alertController.addAction(openEmailAction)
alertController.addAction(cancelAction)
self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
}
@objc private func dontReceiveButtonPressed(_ sender: UIButton) {
let alertController = UIAlertController(title: L10n.Scene.ConfirmEmail.DontReceiveEmail.alertTitle, message: L10n.Scene.ConfirmEmail.DontReceiveEmail.alertDescription, preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) { _ in }
alertController.addAction(okAction)
self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
}
func showEmailAppAlert() {
let clients = ThirdPartyMailClient.clients()
let application = UIApplication.shared
let avaliableClients = clients.filter { client -> Bool in
ThirdPartyMailer.application(application, isMailClientAvailable: client)
}
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let alertAction = UIAlertAction(title: "Mail", style: .default) { _ in
UIApplication.shared.open(URL(string: "message://")!, options: [:], completionHandler: nil)
}
alertController.addAction(alertAction)
_ = avaliableClients.compactMap { client -> UIAlertAction in
let alertAction = UIAlertAction(title: client.name, style: .default) { _ in
_ = ThirdPartyMailer.application(application, openMailClient: client, recipient: nil, subject: nil, body: nil)
}
alertController.addAction(alertAction)
return alertAction
}
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
alertController.addAction(cancelAction)
self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
}
}

View File

@ -0,0 +1,21 @@
//
// MastodonConfirmEmailViewModel.swift
// Mastodon
//
// Created by sxiaojian on 2021/2/23.
//
import Combine
final class MastodonConfirmEmailViewModel {
var disposeBag = Set<AnyCancellable>()
let context: AppContext
var email: String
init(context: AppContext, email: String) {
self.context = context
self.email = email
}
}

View File

@ -572,7 +572,8 @@ extension MastodonRegisterViewController {
let alertController = UIAlertController(title: "Success", message: "Regsiter request sent. Please check your email.\n(Auto sign in not implement yet.)", preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) { [weak self] _ in
guard let self = self else { return }
self.navigationController?.popViewController(animated: true)
let viewModel = MastodonConfirmEmailViewModel(context: self.context, email: email)
self.coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show)
}
alertController.addAction(okAction)
self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))