Merge pull request #80 from tootsuite/fix/serverRulesUI

fix: Update server rules scene UI design
This commit is contained in:
sxiaojian88 2021-04-01 16:00:13 +08:00 committed by GitHub
commit fa14477f7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 186 additions and 33 deletions

View File

@ -162,6 +162,8 @@
"title": "Some ground rules.", "title": "Some ground rules.",
"subtitle": "These rules are set by the admins of %s.", "subtitle": "These rules are set by the admins of %s.",
"prompt": "By continuing, you're subject to the terms of service and privacy policy for %s.", "prompt": "By continuing, you're subject to the terms of service and privacy policy for %s.",
"terms_of_service": "terms of service",
"privacy_policy": "privacy policy",
"button": { "button": {
"confirm": "I Agree" "confirm": "I Agree"
} }

View File

@ -101,6 +101,8 @@
2DF75BB925D1474100694EC8 /* ManagedObjectObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF75BB825D1474100694EC8 /* ManagedObjectObserver.swift */; }; 2DF75BB925D1474100694EC8 /* ManagedObjectObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF75BB825D1474100694EC8 /* ManagedObjectObserver.swift */; };
2DF75BC725D1475D00694EC8 /* ManagedObjectContextObjectsDidChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF75BC625D1475D00694EC8 /* ManagedObjectContextObjectsDidChange.swift */; }; 2DF75BC725D1475D00694EC8 /* ManagedObjectContextObjectsDidChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF75BC625D1475D00694EC8 /* ManagedObjectContextObjectsDidChange.swift */; };
2DFF41892614A4DC00F776A4 /* UIView+Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DFF41882614A4DC00F776A4 /* UIView+Constraint.swift */; }; 2DFF41892614A4DC00F776A4 /* UIView+Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DFF41882614A4DC00F776A4 /* UIView+Constraint.swift */; };
5D0393902612D259007FE196 /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D03938F2612D259007FE196 /* WebViewController.swift */; };
5D0393962612D266007FE196 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0393952612D266007FE196 /* WebViewModel.swift */; };
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; }; 5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; };
5DF1054125F886D400D6C0D4 /* ViedeoPlaybackService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */; }; 5DF1054125F886D400D6C0D4 /* ViedeoPlaybackService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */; };
5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */; }; 5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */; };
@ -410,6 +412,8 @@
3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.debug.xcconfig"; sourceTree = "<group>"; }; 459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.debug.xcconfig"; sourceTree = "<group>"; };
5D03938F2612D259007FE196 /* WebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = "<group>"; };
5D0393952612D266007FE196 /* WebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewModel.swift; sourceTree = "<group>"; };
5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViedeoPlaybackService.swift; sourceTree = "<group>"; }; 5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViedeoPlaybackService.swift; sourceTree = "<group>"; };
5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModel.swift; sourceTree = "<group>"; }; 5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModel.swift; sourceTree = "<group>"; };
5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayer.swift; sourceTree = "<group>"; }; 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayer.swift; sourceTree = "<group>"; };
@ -951,6 +955,15 @@
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5D03938E2612D200007FE196 /* Webview */ = {
isa = PBXGroup;
children = (
5D03938F2612D259007FE196 /* WebViewController.swift */,
5D0393952612D266007FE196 /* WebViewModel.swift */,
);
path = Webview;
sourceTree = "<group>";
};
DB01409B25C40BB600F9F3CF /* Onboarding */ = { DB01409B25C40BB600F9F3CF /* Onboarding */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1328,6 +1341,7 @@
DB8AF55525C1379F002E6C99 /* Scene */ = { DB8AF55525C1379F002E6C99 /* Scene */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5D03938E2612D200007FE196 /* Webview */,
2D7631A425C1532200929FB9 /* Share */, 2D7631A425C1532200929FB9 /* Share */,
DB8AF54E25C13703002E6C99 /* MainTab */, DB8AF54E25C13703002E6C99 /* MainTab */,
DB01409B25C40BB600F9F3CF /* Onboarding */, DB01409B25C40BB600F9F3CF /* Onboarding */,
@ -1860,6 +1874,7 @@
DB447681260B3ED600B66B82 /* CustomEmojiPickerSection.swift in Sources */, DB447681260B3ED600B66B82 /* CustomEmojiPickerSection.swift in Sources */,
0FB3D33225E5F50E00AAD544 /* PickServerSearchCell.swift in Sources */, 0FB3D33225E5F50E00AAD544 /* PickServerSearchCell.swift in Sources */,
DB71FD5225F8CCAA00512AE1 /* APIService+Status.swift in Sources */, DB71FD5225F8CCAA00512AE1 /* APIService+Status.swift in Sources */,
5D0393962612D266007FE196 /* WebViewModel.swift in Sources */,
2D5A3D3825CF8D9F002347D6 /* ScrollViewContainer.swift in Sources */, 2D5A3D3825CF8D9F002347D6 /* ScrollViewContainer.swift in Sources */,
DB1E347825F519300079D7DF /* PickServerItem.swift in Sources */, DB1E347825F519300079D7DF /* PickServerItem.swift in Sources */,
DB1FD45A25F27898004CFCFC /* CategoryPickerItem.swift in Sources */, DB1FD45A25F27898004CFCFC /* CategoryPickerItem.swift in Sources */,
@ -1939,6 +1954,7 @@
2D3F9E0425DFA133004262D9 /* UITapGestureRecognizer.swift in Sources */, 2D3F9E0425DFA133004262D9 /* UITapGestureRecognizer.swift in Sources */,
2D694A7425F9EB4E0038ADDC /* ContentWarningOverlayView.swift in Sources */, 2D694A7425F9EB4E0038ADDC /* ContentWarningOverlayView.swift in Sources */,
DB9A486C26032AC1008B817C /* AttachmentContainerView+EmptyStateView.swift in Sources */, DB9A486C26032AC1008B817C /* AttachmentContainerView+EmptyStateView.swift in Sources */,
5D0393902612D259007FE196 /* WebViewController.swift in Sources */,
DB4481CC25EE2AFE00BEFB67 /* PollItem.swift in Sources */, DB4481CC25EE2AFE00BEFB67 /* PollItem.swift in Sources */,
DB44767B260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift in Sources */, DB44767B260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift in Sources */,
DB4563BD25E11A24004DA0B9 /* KeyboardResponderService.swift in Sources */, DB4563BD25E11A24004DA0B9 /* KeyboardResponderService.swift in Sources */,

View File

@ -46,6 +46,7 @@ extension SceneCoordinator {
case mastodonServerRules(viewModel: MastodonServerRulesViewModel) case mastodonServerRules(viewModel: MastodonServerRulesViewModel)
case mastodonConfirmEmail(viewModel: MastodonConfirmEmailViewModel) case mastodonConfirmEmail(viewModel: MastodonConfirmEmailViewModel)
case mastodonResendEmail(viewModel: MastodonResendEmailViewModel) case mastodonResendEmail(viewModel: MastodonResendEmailViewModel)
case mastodonWebView(viewModel:WebViewModel)
// compose // compose
case compose(viewModel: ComposeViewModel) case compose(viewModel: ComposeViewModel)
@ -193,6 +194,10 @@ private extension SceneCoordinator {
let _viewController = MastodonResendEmailViewController() let _viewController = MastodonResendEmailViewController()
_viewController.viewModel = viewModel _viewController.viewModel = viewModel
viewController = _viewController viewController = _viewController
case .mastodonWebView(let viewModel):
let _viewController = WebViewController()
_viewController.viewModel = viewModel
viewController = _viewController
case .compose(let viewModel): case .compose(let viewModel):
let _viewController = ComposeViewController() let _viewController = ComposeViewController()
_viewController.viewModel = viewModel _viewController.viewModel = viewModel

View File

@ -410,6 +410,8 @@ internal enum L10n {
} }
} }
internal enum ServerRules { internal enum ServerRules {
/// privacy policy
internal static let privacyPolicy = L10n.tr("Localizable", "Scene.ServerRules.PrivacyPolicy")
/// By continuing, you're subject to the terms of service and privacy policy for %@. /// By continuing, you're subject to the terms of service and privacy policy for %@.
internal static func prompt(_ p1: Any) -> String { internal static func prompt(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.ServerRules.Prompt", String(describing: p1)) return L10n.tr("Localizable", "Scene.ServerRules.Prompt", String(describing: p1))
@ -418,6 +420,8 @@ internal enum L10n {
internal static func subtitle(_ p1: Any) -> String { internal static func subtitle(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.ServerRules.Subtitle", String(describing: p1)) return L10n.tr("Localizable", "Scene.ServerRules.Subtitle", String(describing: p1))
} }
/// terms of service
internal static let termsOfService = L10n.tr("Localizable", "Scene.ServerRules.TermsOfService")
/// Some ground rules. /// Some ground rules.
internal static let title = L10n.tr("Localizable", "Scene.ServerRules.Title") internal static let title = L10n.tr("Localizable", "Scene.ServerRules.Title")
internal enum Button { internal enum Button {

View File

@ -129,8 +129,10 @@ tap the link to confirm your account.";
"Scene.ServerPicker.Title" = "Pick a Server, "Scene.ServerPicker.Title" = "Pick a Server,
any server."; any server.";
"Scene.ServerRules.Button.Confirm" = "I Agree"; "Scene.ServerRules.Button.Confirm" = "I Agree";
"Scene.ServerRules.PrivacyPolicy" = "privacy policy";
"Scene.ServerRules.Prompt" = "By continuing, you're subject to the terms of service and privacy policy for %@."; "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.Subtitle" = "These rules are set by the admins of %@.";
"Scene.ServerRules.TermsOfService" = "terms of service";
"Scene.ServerRules.Title" = "Some ground rules."; "Scene.ServerRules.Title" = "Some ground rules.";
"Scene.Welcome.Slogan" = "Social networking "Scene.Welcome.Slogan" = "Social networking
back in your hands."; back in your hands.";

View File

@ -8,6 +8,8 @@
import os.log import os.log
import UIKit import UIKit
import Combine import Combine
import MastodonSDK
import SafariServices
final class MastodonServerRulesViewController: UIViewController, NeedsDependency { final class MastodonServerRulesViewController: UIViewController, NeedsDependency {
@ -44,19 +46,20 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
return label return label
}() }()
let bottonContainerView: UIView = { let bottomContainerView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = Asset.Colors.Background.onboardingBackground.color view.backgroundColor = Asset.Colors.Background.onboardingBackground.color
return view return view
}() }()
private(set) lazy var bottomPromptLabel: UILabel = { private(set) lazy var bottomPromptTextView: UITextView = {
let label = UILabel() let textView = UITextView()
label.font = .preferredFont(forTextStyle: .body) textView.font = .preferredFont(forTextStyle: .body)
label.textColor = .label textView.textColor = .label
label.text = L10n.Scene.ServerRules.prompt(viewModel.domain) textView.isSelectable = true
label.numberOfLines = 0 textView.isEditable = false
return label textView.backgroundColor = Asset.Colors.Background.onboardingBackground.color
return textView
}() }()
let confirmButton: PrimaryActionButton = { let confirmButton: PrimaryActionButton = {
@ -86,36 +89,39 @@ extension MastodonServerRulesViewController {
super.viewDidLoad() super.viewDidLoad()
setupOnboardingAppearance() setupOnboardingAppearance()
configTextView()
defer { setupNavigationBarBackgroundView() } defer { setupNavigationBarBackgroundView() }
bottonContainerView.translatesAutoresizingMaskIntoConstraints = false bottomContainerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bottonContainerView) view.addSubview(bottomContainerView)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
view.bottomAnchor.constraint(equalTo: bottonContainerView.bottomAnchor), view.bottomAnchor.constraint(equalTo: bottomContainerView.bottomAnchor),
bottonContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), bottomContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
bottonContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), bottomContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
]) ])
bottonContainerView.preservesSuperviewLayoutMargins = true bottomContainerView.preservesSuperviewLayoutMargins = true
defer { defer {
view.bringSubviewToFront(bottonContainerView) view.bringSubviewToFront(bottomContainerView)
} }
confirmButton.translatesAutoresizingMaskIntoConstraints = false confirmButton.translatesAutoresizingMaskIntoConstraints = false
bottonContainerView.addSubview(confirmButton) bottomContainerView.addSubview(confirmButton)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
bottonContainerView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: confirmButton.bottomAnchor, constant: MastodonServerRulesViewController.viewBottomPaddingHeight), bottomContainerView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: confirmButton.bottomAnchor, constant: MastodonServerRulesViewController.viewBottomPaddingHeight),
confirmButton.leadingAnchor.constraint(equalTo: bottonContainerView.readableContentGuide.leadingAnchor, constant: MastodonServerRulesViewController.actionButtonMargin), confirmButton.leadingAnchor.constraint(equalTo: bottomContainerView.readableContentGuide.leadingAnchor, constant: MastodonServerRulesViewController.actionButtonMargin),
bottonContainerView.readableContentGuide.trailingAnchor.constraint(equalTo: confirmButton.trailingAnchor, constant: MastodonServerRulesViewController.actionButtonMargin), bottomContainerView.readableContentGuide.trailingAnchor.constraint(equalTo: confirmButton.trailingAnchor, constant: MastodonServerRulesViewController.actionButtonMargin),
confirmButton.heightAnchor.constraint(equalToConstant: MastodonServerRulesViewController.actionButtonHeight).priority(.defaultHigh), confirmButton.heightAnchor.constraint(equalToConstant: MastodonServerRulesViewController.actionButtonHeight).priority(.defaultHigh),
]) ])
bottomPromptLabel.translatesAutoresizingMaskIntoConstraints = false bottomPromptTextView.translatesAutoresizingMaskIntoConstraints = false
bottonContainerView.addSubview(bottomPromptLabel) bottomContainerView.addSubview(bottomPromptTextView)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
bottomPromptLabel.topAnchor.constraint(equalTo: bottonContainerView.topAnchor, constant: 20), bottomPromptTextView.frameLayoutGuide.topAnchor.constraint(equalTo: bottomContainerView.topAnchor, constant: 20),
bottomPromptLabel.leadingAnchor.constraint(equalTo: bottonContainerView.readableContentGuide.leadingAnchor), bottomPromptTextView.frameLayoutGuide.leadingAnchor.constraint(equalTo: bottomContainerView.readableContentGuide.leadingAnchor),
bottomPromptLabel.trailingAnchor.constraint(equalTo: bottonContainerView.readableContentGuide.trailingAnchor), bottomPromptTextView.frameLayoutGuide.trailingAnchor.constraint(equalTo: bottomContainerView.readableContentGuide.trailingAnchor),
confirmButton.topAnchor.constraint(equalTo: bottomPromptLabel.bottomAnchor, constant: 20), bottomPromptTextView.frameLayoutGuide.heightAnchor.constraint(equalToConstant: 50),
confirmButton.topAnchor.constraint(equalTo: bottomPromptTextView.frameLayoutGuide.bottomAnchor, constant: 20),
]) ])
scrollView.translatesAutoresizingMaskIntoConstraints = false scrollView.translatesAutoresizingMaskIntoConstraints = false
@ -165,8 +171,32 @@ extension MastodonServerRulesViewController {
extension MastodonServerRulesViewController { extension MastodonServerRulesViewController {
func updateScrollViewContentInset() { func updateScrollViewContentInset() {
view.layoutIfNeeded() view.layoutIfNeeded()
scrollView.contentInset.bottom = bottonContainerView.frame.height scrollView.contentInset.bottom = bottomContainerView.frame.height
scrollView.verticalScrollIndicatorInsets.bottom = bottonContainerView.frame.height scrollView.verticalScrollIndicatorInsets.bottom = bottomContainerView.frame.height
}
func configTextView() {
let linkColor = Asset.Colors.Button.normal.color
let str = NSString(string: L10n.Scene.ServerRules.prompt(viewModel.domain))
let termsOfServiceRange = str.range(of: L10n.Scene.ServerRules.termsOfService)
let privacyRange = str.range(of: L10n.Scene.ServerRules.privacyPolicy)
let attributeString = NSMutableAttributedString(string: L10n.Scene.ServerRules.prompt(viewModel.domain), attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .body), NSAttributedString.Key.foregroundColor: UIColor.label])
attributeString.addAttribute(.link, value: Mastodon.API.serverRulesURL(domain: viewModel.domain), range: termsOfServiceRange)
attributeString.addAttribute(.link, value: Mastodon.API.privacyURL(domain: viewModel.domain), range: privacyRange)
let linkAttributes = [NSAttributedString.Key.foregroundColor:linkColor]
bottomPromptTextView.attributedText = attributeString
bottomPromptTextView.linkTextAttributes = linkAttributes
bottomPromptTextView.delegate = self
}
}
extension MastodonServerRulesViewController: UITextViewDelegate {
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
let safariVC = SFSafariViewController(url: URL)
self.present(safariVC, animated: true, completion: nil)
return false
} }
} }

View File

@ -35,13 +35,16 @@ final class MastodonServerRulesViewModel {
var rulesAttributedString: NSAttributedString { var rulesAttributedString: NSAttributedString {
let attributedString = NSMutableAttributedString(string: "\n") let attributedString = NSMutableAttributedString(string: "\n")
let configuration = UIImage.SymbolConfiguration(font: .preferredFont(forTextStyle: .title3))
for (i, rule) in rules.enumerated() { for (i, rule) in rules.enumerated() {
let index = String(i + 1) let imageName = String(i + 1) + ".circle.fill"
let indexString = NSAttributedString(string: index + ". ", attributes: [ let image = UIImage(systemName: imageName, withConfiguration: configuration)!
NSAttributedString.Key.foregroundColor: UIColor.secondaryLabel let attachment = NSTextAttachment()
]) attachment.image = image.withTintColor(.black)
let ruleString = NSAttributedString(string: rule.text + "\n\n") let imageAttribute = NSAttributedString(attachment: attachment)
attributedString.append(indexString)
let ruleString = NSAttributedString(string: " " + rule.text + "\n\n")
attributedString.append(imageAttribute)
attributedString.append(ruleString) attributedString.append(ruleString)
} }
return attributedString return attributedString

View File

@ -0,0 +1,67 @@
//
// WebViewController.swift
// Mastodon
//
// Created by xiaojian sun on 2021/3/30.
//
import Foundation
import Combine
import os.log
import UIKit
import WebKit
final class WebViewController: UIViewController, NeedsDependency {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>()
var viewModel: WebViewModel!
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 WebViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(WebViewController.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.url)
webView.load(request)
}
}
extension WebViewController {
@objc private func cancelBarButtonItemPressed(_ sender: UIBarButtonItem) {
dismiss(animated: true, completion: nil)
}
}

View File

@ -0,0 +1,17 @@
//
// WebViewModel.swift
// Mastodon
//
// Created by xiaojian sun on 2021/3/30.
//
import Foundation
final class WebViewModel {
public init(url: URL) {
self.url = url
}
// input
let url: URL
}

View File

@ -89,6 +89,13 @@ extension Mastodon.API {
return URL(string: "https://" + domain + "/auth/confirmation/new")! return URL(string: "https://" + domain + "/auth/confirmation/new")!
} }
public static func serverRulesURL(domain: String) -> URL {
return URL(string: "https://" + domain + "/about/more")!
}
public static func privacyURL(domain: String) -> URL {
return URL(string: "https://" + domain + "/terms")!
}
} }
extension Mastodon.API { extension Mastodon.API {