diff --git a/Localization/app.json b/Localization/app.json index bfe2178cb..19bf46537 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -63,7 +63,9 @@ "prompt": "Your password needs at least:", "prompt_eight_characters": "Eight characters" } - } + }, + "success": "Success", + "check_email": "Regsiter request sent. Please check your email." }, "server_rules": { "title": "Some ground rules.", diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 63aacb372..b4eb91e4f 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ 2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */; }; 2D32EABA25CB9B0500C9ED86 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAB925CB9B0500C9ED86 /* UIView.swift */; }; 2D32EADA25CBCC3300C9ED86 /* PublicTimelineViewModel+LoadMiddleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAD925CBCC3300C9ED86 /* PublicTimelineViewModel+LoadMiddleState.swift */; }; + 2D364F7225E66D7500204FDC /* MastodonResendEmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D364F7125E66D7500204FDC /* MastodonResendEmailViewController.swift */; }; + 2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D364F7725E66D8300204FDC /* MastodonResendEmailViewModel.swift */; }; 2D38F1C625CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D38F1C525CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift */; }; 2D38F1D525CD465300561493 /* HomeTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D38F1D425CD465300561493 /* HomeTimelineViewController.swift */; }; 2D38F1DF25CD46A400561493 /* HomeTimelineViewController+StatusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D38F1DE25CD46A400561493 /* HomeTimelineViewController+StatusProvider.swift */; }; @@ -213,6 +215,8 @@ 2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMiddleLoaderTableViewCell.swift; sourceTree = ""; }; 2D32EAB925CB9B0500C9ED86 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; 2D32EAD925CBCC3300C9ED86 /* PublicTimelineViewModel+LoadMiddleState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PublicTimelineViewModel+LoadMiddleState.swift"; sourceTree = ""; }; + 2D364F7125E66D7500204FDC /* MastodonResendEmailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewController.swift; sourceTree = ""; }; + 2D364F7725E66D8300204FDC /* MastodonResendEmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewModel.swift; sourceTree = ""; }; 2D38F1C525CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentOffsetAdjustableTimelineViewControllerDelegate.swift; sourceTree = ""; }; 2D38F1D425CD465300561493 /* HomeTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineViewController.swift; sourceTree = ""; }; 2D38F1DE25CD46A400561493 /* HomeTimelineViewController+StatusProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HomeTimelineViewController+StatusProvider.swift"; sourceTree = ""; }; @@ -446,6 +450,15 @@ path = Content; sourceTree = ""; }; + 2D364F7025E66D5B00204FDC /* ResendEmail */ = { + isa = PBXGroup; + children = ( + 2D364F7125E66D7500204FDC /* MastodonResendEmailViewController.swift */, + 2D364F7725E66D8300204FDC /* MastodonResendEmailViewModel.swift */, + ); + path = ResendEmail; + sourceTree = ""; + }; 2D38F1D325CD463600561493 /* HomeTimeline */ = { isa = PBXGroup; children = ( @@ -631,6 +644,7 @@ DB01409B25C40BB600F9F3CF /* Authentication */ = { isa = PBXGroup; children = ( + 2D364F7025E66D5B00204FDC /* ResendEmail */, 2D59819925E4A55C000FB903 /* ConfirmEmail */, DB0140A625C40C0900F9F3CF /* PinBased */, DBE0821A25CD382900FD6BBD /* Register */, @@ -1353,6 +1367,7 @@ 0FAA101225E105390017CCDE /* PrimaryActionButton.swift in Sources */, DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */, DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */, + 2D364F7225E66D7500204FDC /* MastodonResendEmailViewController.swift in Sources */, 2D38F1F125CD477D00561493 /* HomeTimelineViewModel+LoadMiddleState.swift in Sources */, DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */, 2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */, @@ -1395,6 +1410,7 @@ DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */, DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */, 2D38F1C625CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift in Sources */, + 2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */, DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */, DB9D6BF825E4F5690051B173 /* NotificationViewController.swift in Sources */, DB45FADD25CA6F6B005A8AC7 /* APIService+CoreData+MastodonUser.swift in Sources */, diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index ef04c9572..30b68ce5e 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -44,6 +44,7 @@ extension SceneCoordinator { case mastodonRegister(viewModel: MastodonRegisterViewModel) case mastodonServerRules(viewModel: MastodonServerRulesViewModel) case mastodonConfirmEmail(viewModel: MastodonConfirmEmailViewModel) + case mastodonResendEmail(viewModel: MastodonResendEmailViewModel) case alertController(alertController: UIAlertController) } @@ -157,6 +158,10 @@ private extension SceneCoordinator { let _viewController = MastodonConfirmEmailViewController() _viewController.viewModel = viewModel viewController = _viewController + case .mastodonResendEmail(let viewModel): + let _viewController = MastodonResendEmailViewController() + _viewController.viewModel = viewModel + viewController = _viewController case .alertController(let alertController): if let popoverPresentationController = alertController.popoverPresentationController { assert( diff --git a/Mastodon/Generated/Strings.swift b/Mastodon/Generated/Strings.swift index 975b72037..a5cd7a9d7 100644 --- a/Mastodon/Generated/Strings.swift +++ b/Mastodon/Generated/Strings.swift @@ -85,7 +85,7 @@ internal enum L10n { 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 haven’t. + /// Give our servers another n seconds before sending another email.\nCheck if your email address is correct as well as your junk folder if you haven’t. internal static let alertDescription = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.AlertDescription") /// It’s too soon to tell. internal static let alertTitle = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.AlertTitle") @@ -108,6 +108,10 @@ internal enum L10n { internal static let title = L10n.tr("Localizable", "Scene.PublicTimeline.Title") } internal enum Register { + /// Regsiter request sent. Please check your email. + internal static let checkEmail = L10n.tr("Localizable", "Scene.Register.CheckEmail") + /// Success + internal static let success = L10n.tr("Localizable", "Scene.Register.Success") /// Tell us about you. internal static let title = L10n.tr("Localizable", "Scene.Register.Title") internal enum Input { diff --git a/Mastodon/Resources/en.lproj/Localizable.strings b/Mastodon/Resources/en.lproj/Localizable.strings index 75dc3999b..c9c731c6b 100644 --- a/Mastodon/Resources/en.lproj/Localizable.strings +++ b/Mastodon/Resources/en.lproj/Localizable.strings @@ -19,8 +19,20 @@ "Common.Controls.Timeline.LoadMore" = "Load More"; "Common.Countable.Photo.Multiple" = "photos"; "Common.Countable.Photo.Single" = "photo"; +"Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email"; +"Scene.ConfirmEmail.Button.OpenEmailApp" = "Open email app"; +"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 haven’t."; +"Scene.ConfirmEmail.DontReceiveEmail.AlertTitle" = "It’s too soon to tell."; +"Scene.ConfirmEmail.OpenEmailApp.AlertDescription" = "We just sent you another email. Check your junk folder if you haven’t."; +"Scene.ConfirmEmail.OpenEmailApp.AlertTitle" = "Check your inbox."; +"Scene.ConfirmEmail.OpenEmailApp.Mail" = "Mail"; +"Scene.ConfirmEmail.Subtitle" = "We just sent an email to %@, +tap the link to confirm your account."; +"Scene.ConfirmEmail.Title" = "One last thing."; "Scene.HomeTimeline.Title" = "Home"; "Scene.PublicTimeline.Title" = "Public"; +"Scene.Register.CheckEmail" = "Regsiter request sent. Please check your email."; "Scene.Register.Input.DisplayName.Placeholder" = "display name"; "Scene.Register.Input.Email.Placeholder" = "email"; "Scene.Register.Input.Password.Placeholder" = "password"; @@ -28,6 +40,7 @@ "Scene.Register.Input.Password.PromptEightCharacters" = "Eight characters"; "Scene.Register.Input.Username.DuplicatePrompt" = "This username is taken."; "Scene.Register.Input.Username.Placeholder" = "username"; +"Scene.Register.Success" = "Success"; "Scene.Register.Title" = "Tell us about you."; "Scene.ServerPicker.Input.Placeholder" = "Find a server or join your own..."; "Scene.ServerPicker.Title" = "Pick a Server, diff --git a/Mastodon/Scene/Authentication/AuthenticationViewController.swift b/Mastodon/Scene/Authentication/AuthenticationViewController.swift index 5ab500bc2..d93651ba5 100644 --- a/Mastodon/Scene/Authentication/AuthenticationViewController.swift +++ b/Mastodon/Scene/Authentication/AuthenticationViewController.swift @@ -78,7 +78,7 @@ extension AuthenticationViewController { super.viewDidLoad() overrideUserInterfaceStyle = .dark // FIXME: - title = "Authentication" + view.backgroundColor = Asset.Colors.Background.systemBackground.color domainLabel.translatesAutoresizingMaskIntoConstraints = false diff --git a/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewController.swift b/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewController.swift index 4a0cec74a..ff41740b2 100644 --- a/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewController.swift +++ b/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewController.swift @@ -8,6 +8,7 @@ import Combine import ThirdPartyMailer import UIKit +import MastodonSDK final class MastodonConfirmEmailViewController: UIViewController, NeedsDependency { var disposeBag = Set() @@ -58,18 +59,25 @@ final class MastodonConfirmEmailViewController: UIViewController, NeedsDependenc } extension MastodonConfirmEmailViewController { + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.setNavigationBarHidden(false, animated: false) + } 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" + let barAppearance = UINavigationBarAppearance() + barAppearance.configureWithTransparentBackground() + navigationController?.navigationBar.standardAppearance = barAppearance + navigationController?.navigationBar.compactAppearance = barAppearance + navigationController?.navigationBar.scrollEdgeAppearance = barAppearance // resizedView let resizedView = UIView() resizedView.translatesAutoresizingMaskIntoConstraints = false - resizedView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical) + resizedView.setContentHuggingPriority(.defaultLow, for: .vertical) // stackView let stackView = UIStackView() @@ -114,7 +122,11 @@ extension MastodonConfirmEmailViewController { @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 } + let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) { _ in + let url = Mastodon.API.resendEmailURL(domain:self.viewModel.authenticateInfo.domain) + let viewModel = MastodonResendEmailViewModel(resendEmailURL: url, email: self.viewModel.email) + self.coordinator.present(scene: .mastodonResendEmail(viewModel: viewModel), from: self, transition: .show) + } alertController.addAction(okAction) self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil)) } diff --git a/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewModel.swift b/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewModel.swift index 8da02d8ed..d1e3936a2 100644 --- a/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewModel.swift +++ b/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewModel.swift @@ -6,16 +6,48 @@ // import Combine +import Foundation +import MastodonSDK +import os.log final class MastodonConfirmEmailViewModel { var disposeBag = Set() - + let context: AppContext + let coordinator: SceneCoordinator var email: String - - init(context: AppContext, email: String) { + let authenticateInfo: AuthenticationViewModel.AuthenticateInfo + let userToken: Mastodon.Entity.Token + + let timestampUpdatePublisher = Timer.publish(every: 4.0, on: .main, in: .common) + .autoconnect() + .share() + .eraseToAnyPublisher() + + init(context: AppContext,coordinator: SceneCoordinator, email: String, authenticateInfo: AuthenticationViewModel.AuthenticateInfo, userToken: Mastodon.Entity.Token) { self.context = context + self.coordinator = coordinator self.email = email + self.authenticateInfo = authenticateInfo + self.userToken = userToken + timestampUpdatePublisher + .sink { [weak self] _ in + guard let self = self else { return } + AuthenticationViewModel.verifyAndSaveAuthentication(context: self.context, info: authenticateInfo, userToken: userToken) + .receive(on: DispatchQueue.main) + .sink { completion in + switch completion { + case .failure(let error): + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: swap user access token swap fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription) + case .finished: + break + } + } receiveValue: { _ in + self.coordinator.setup() + } + .store(in: &self.disposeBag) + + } + .store(in: &disposeBag) } } - diff --git a/Mastodon/Scene/Authentication/Register/MastodonRegisterViewController.swift b/Mastodon/Scene/Authentication/Register/MastodonRegisterViewController.swift index afdf815ae..1ee306cf1 100644 --- a/Mastodon/Scene/Authentication/Register/MastodonRegisterViewController.swift +++ b/Mastodon/Scene/Authentication/Register/MastodonRegisterViewController.swift @@ -567,12 +567,12 @@ extension MastodonRegisterViewController { } } receiveValue: { [weak self] response in guard let self = self else { return } - _ = response.value + let userToken = response.value // TODO: - let alertController = UIAlertController(title: "Success", message: "Regsiter request sent. Please check your email.\n(Auto sign in not implement yet.)", preferredStyle: .alert) + let alertController = UIAlertController(title: L10n.Scene.Register.success, message: L10n.Scene.Register.checkEmail, preferredStyle: .alert) let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) { [weak self] _ in guard let self = self else { return } - let viewModel = MastodonConfirmEmailViewModel(context: self.context, email: email) + let viewModel = MastodonConfirmEmailViewModel(context: self.context, coordinator: self.coordinator, email: email, authenticateInfo: self.viewModel.authenticateInfo, userToken: userToken) self.coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show) } alertController.addAction(okAction) @@ -580,5 +580,4 @@ extension MastodonRegisterViewController { } .store(in: &disposeBag) } - } diff --git a/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewController.swift b/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewController.swift new file mode 100644 index 000000000..e54baa137 --- /dev/null +++ b/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewController.swift @@ -0,0 +1,73 @@ +// +// MastodonResendEmailViewController.swift +// Mastodon +// +// Created by sxiaojian on 2021/2/24. +// + +import Combine +import os.log +import UIKit +import WebKit + +final class MastodonResendEmailViewController: UIViewController, NeedsDependency, WKNavigationDelegate { + weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } + weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } + + var disposeBag = Set() + var viewModel: MastodonResendEmailViewModel! + + let webView: WKWebView = { + let configuration = WKWebViewConfiguration() + configuration.processPool = WKProcessPool() + let webView = WKWebView(frame: .zero, configuration: configuration) + return webView + }() + + deinit { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", (#file as NSString).lastPathComponent, #line, #function) + + // cleanup cookie + let httpCookieStore = webView.configuration.websiteDataStore.httpCookieStore + httpCookieStore.getAllCookies { cookies in + for cookie in cookies { + httpCookieStore.delete(cookie, completionHandler: nil) + } + } + } +} + +extension MastodonResendEmailViewController { + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(MastodonResendEmailViewController.cancelBarButtonItemPressed(_:))) + + webView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(webView) + NSLayoutConstraint.activate([ + webView.topAnchor.constraint(equalTo: view.topAnchor), + webView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + webView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + webView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + ]) + + let request = URLRequest(url: viewModel.resendEmailURL) + webView.navigationDelegate = self + webView.load(request) + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: resendEmail via: %s", (#file as NSString).lastPathComponent, #line, #function, viewModel.resendEmailURL.debugDescription) + } +} + +extension MastodonResendEmailViewController { + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + let scriptString = "document.getElementById('user_email').value = '\(self.viewModel.email)';" + webView.evaluateJavaScript(scriptString) + } +} + +extension MastodonResendEmailViewController { + @objc private func cancelBarButtonItemPressed(_ sender: UIBarButtonItem) { + dismiss(animated: true, completion: nil) + } +} diff --git a/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewModel.swift b/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewModel.swift new file mode 100644 index 000000000..861d7d7f7 --- /dev/null +++ b/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewModel.swift @@ -0,0 +1,26 @@ +// +// MastodonResendEmailViewModel.swift +// Mastodon +// +// Created by sxiaojian on 2021/2/24. +// + +import Combine +import Foundation +import os.log +import WebKit + +final class MastodonResendEmailViewModel { + // input + let resendEmailURL: URL + let email: String + + init(resendEmailURL: URL, email: String) { + self.resendEmailURL = resendEmailURL + self.email = email + } + + deinit { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", (#file as NSString).lastPathComponent, #line, #function) + } +} diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift index 24b7ec3ad..92897090c 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift @@ -81,6 +81,10 @@ extension Mastodon.API { static let joinMastodonEndpointURL = URL(string: "https://api.joinmastodon.org/")! + public static func resendEmailURL(domain: String) -> URL { + return URL(string: "https://" + domain + "/auth/confirmation/new")! + } + } extension Mastodon.API {