mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-24 09:10:59 +00:00
Official account and website in about
This commit is contained in:
parent
8e48a8bab8
commit
187bc42373
12 changed files with 164 additions and 80 deletions
|
@ -1,5 +1,6 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import SafariServices
|
||||
import UIKit
|
||||
import ViewModels
|
||||
|
||||
|
@ -16,4 +17,26 @@ extension UIViewController {
|
|||
|
||||
present(alertController, animated: true)
|
||||
}
|
||||
|
||||
#if !IS_SHARE_EXTENSION
|
||||
func open(url: URL, identityContext: IdentityContext) {
|
||||
func openWithRegardToBrowserSetting(url: URL) {
|
||||
if identityContext.appPreferences.openLinksInDefaultBrowser || !url.isHTTPURL {
|
||||
UIApplication.shared.open(url)
|
||||
} else {
|
||||
present(SFSafariViewController(url: url), animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
if identityContext.appPreferences.useUniversalLinks {
|
||||
UIApplication.shared.open(url, options: [.universalLinksOnly: true]) { success in
|
||||
if !success {
|
||||
openWithRegardToBrowserSetting(url: url)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
openWithRegardToBrowserSetting(url: url)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
"about" = "About";
|
||||
"about.acknowledgments" = "Acknowledgments";
|
||||
"about.made-by-metabolist" = "Made by Metabolist";
|
||||
"about.official-account" = "Official Account";
|
||||
"about.website" = "Website";
|
||||
"accessibility.activate-link-%@" = "Activate link: %@";
|
||||
"accessibility.copy-text" = "Copy text";
|
||||
"account.%@-followers" = "%@'s Followers";
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
D0030982250C6C8500EACB32 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0030981250C6C8500EACB32 /* URL+Extensions.swift */; };
|
||||
D005A1D825EF189A008B2E63 /* ReportViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D005A1D725EF189A008B2E63 /* ReportViewController.swift */; };
|
||||
D005A1E625EF3D11008B2E63 /* ReportHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D005A1E525EF3D11008B2E63 /* ReportHeaderView.swift */; };
|
||||
D005A20025EF574F008B2E63 /* NavigationHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = D005A1FF25EF574F008B2E63 /* NavigationHandling.swift */; };
|
||||
D005A20125EF574F008B2E63 /* NavigationHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = D005A1FF25EF574F008B2E63 /* NavigationHandling.swift */; };
|
||||
D00702292555E51200F38136 /* ConversationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00702282555E51200F38136 /* ConversationTableViewCell.swift */; };
|
||||
D00702312555F4AE00F38136 /* ConversationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00702302555F4AE00F38136 /* ConversationView.swift */; };
|
||||
D00702362555F4C500F38136 /* ConversationContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00702352555F4C500F38136 /* ConversationContentConfiguration.swift */; };
|
||||
|
@ -262,6 +264,7 @@
|
|||
D0030981250C6C8500EACB32 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; };
|
||||
D005A1D725EF189A008B2E63 /* ReportViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportViewController.swift; sourceTree = "<group>"; };
|
||||
D005A1E525EF3D11008B2E63 /* ReportHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportHeaderView.swift; sourceTree = "<group>"; };
|
||||
D005A1FF25EF574F008B2E63 /* NavigationHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationHandling.swift; sourceTree = "<group>"; };
|
||||
D00702282555E51200F38136 /* ConversationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D00702302555F4AE00F38136 /* ConversationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationView.swift; sourceTree = "<group>"; };
|
||||
D00702352555F4C500F38136 /* ConversationContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationContentConfiguration.swift; sourceTree = "<group>"; };
|
||||
|
@ -735,6 +738,7 @@
|
|||
D0C7D42024F76169001EBDBB /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D005A1FF25EF574F008B2E63 /* NavigationHandling.swift */,
|
||||
D021A66425C3E170008A0C0D /* SwiftUI */,
|
||||
D021A66325C3E167008A0C0D /* UIKit */,
|
||||
D0EA59472522B8B600804347 /* ViewConstants.swift */,
|
||||
|
@ -1084,6 +1088,7 @@
|
|||
D0E9F9AA258450B300EF503D /* CompositionInputAccessoryView.swift in Sources */,
|
||||
D021A60A25C36B32008A0C0D /* IdentityTableViewCell.swift in Sources */,
|
||||
D0849C7F25903C4900A5EBCC /* Status+Extensions.swift in Sources */,
|
||||
D005A20025EF574F008B2E63 /* NavigationHandling.swift in Sources */,
|
||||
D0F4362D25C10B9600E4F896 /* AddIdentityViewController.swift in Sources */,
|
||||
D035D8F925E4338D00E597C9 /* ImageDiskCache.swift in Sources */,
|
||||
D0625E59250F092900502611 /* StatusTableViewCell.swift in Sources */,
|
||||
|
@ -1203,6 +1208,7 @@
|
|||
D059373425AAEA7000754FDF /* CompositionPollView.swift in Sources */,
|
||||
D021A67B25C3E32A008A0C0D /* PlayerView.swift in Sources */,
|
||||
D052DBDD25EAF01800FFB628 /* URL+Extensions.swift in Sources */,
|
||||
D005A20125EF574F008B2E63 /* NavigationHandling.swift in Sources */,
|
||||
D021A69025C3E4B8008A0C0D /* EmojiContentConfiguration.swift in Sources */,
|
||||
D08E52D2257C811200FA2C5F /* ShareExtensionError+Extensions.swift in Sources */,
|
||||
D00CB23D25C9305D008EF267 /* NSMutableAttributedString+Extensions.swift in Sources */,
|
||||
|
|
|
@ -5,6 +5,7 @@ import UIKit
|
|||
import ViewModels
|
||||
|
||||
final class ExploreViewController: UICollectionViewController {
|
||||
private let webfingerIndicatorView = WebfingerIndicatorView()
|
||||
private let viewModel: ExploreViewModel
|
||||
private let rootViewModel: RootViewModel
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
@ -30,6 +31,7 @@ final class ExploreViewController: UICollectionViewController {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// swiftlint:disable:next function_body_length
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
@ -60,6 +62,9 @@ final class ExploreViewController: UICollectionViewController {
|
|||
searchController.searchBar.keyboardType = .twitter
|
||||
navigationItem.searchController = searchController
|
||||
|
||||
view.addSubview(webfingerIndicatorView)
|
||||
webfingerIndicatorView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
viewModel.identityContext.$appPreferences.sink { appPreferences in
|
||||
searchController.searchBar.scopeButtonTitles = SearchScope.allCases.map {
|
||||
$0.title(statusWord: appPreferences.statusWord)
|
||||
|
@ -67,6 +72,11 @@ final class ExploreViewController: UICollectionViewController {
|
|||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
webfingerIndicatorView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
|
||||
webfingerIndicatorView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor)
|
||||
])
|
||||
|
||||
viewModel.events.sink { [weak self] in self?.handle(event: $0) }.store(in: &cancellables)
|
||||
|
||||
viewModel.$loading.sink { [weak self] in
|
||||
|
@ -126,6 +136,43 @@ extension ExploreViewController: ScrollableToTop {
|
|||
}
|
||||
}
|
||||
|
||||
extension ExploreViewController: NavigationHandling {
|
||||
func handle(navigation: Navigation) {
|
||||
switch navigation {
|
||||
case let .collection(collectionService):
|
||||
let vc = TableViewController(
|
||||
viewModel: CollectionItemsViewModel(
|
||||
collectionService: collectionService,
|
||||
identityContext: viewModel.identityContext),
|
||||
rootViewModel: rootViewModel,
|
||||
parentNavigationController: nil)
|
||||
|
||||
show(vc, sender: self)
|
||||
webfingerIndicatorView.stopAnimating()
|
||||
case let .profile(profileService):
|
||||
let vc = ProfileViewController(
|
||||
viewModel: ProfileViewModel(
|
||||
profileService: profileService,
|
||||
identityContext: viewModel.identityContext),
|
||||
rootViewModel: rootViewModel,
|
||||
identityContext: viewModel.identityContext,
|
||||
parentNavigationController: nil)
|
||||
|
||||
show(vc, sender: self)
|
||||
webfingerIndicatorView.stopAnimating()
|
||||
case let .url(url):
|
||||
open(url: url, identityContext: viewModel.identityContext)
|
||||
webfingerIndicatorView.stopAnimating()
|
||||
case .webfingerStart:
|
||||
webfingerIndicatorView.startAnimating()
|
||||
case .webfingerEnd:
|
||||
webfingerIndicatorView.stopAnimating()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension ExploreViewController {
|
||||
static let bottomInset: CGFloat = .newStatusButtonDimension + .defaultSpacing * 4
|
||||
|
||||
|
@ -152,20 +199,4 @@ private extension ExploreViewController {
|
|||
handle(navigation: navigation)
|
||||
}
|
||||
}
|
||||
|
||||
func handle(navigation: Navigation) {
|
||||
switch navigation {
|
||||
case let .collection(collectionService):
|
||||
let vc = TableViewController(
|
||||
viewModel: CollectionItemsViewModel(
|
||||
collectionService: collectionService,
|
||||
identityContext: viewModel.identityContext),
|
||||
rootViewModel: rootViewModel,
|
||||
parentNavigationController: nil)
|
||||
|
||||
show(vc, sender: self)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ final class MainNavigationViewController: UITabBarController {
|
|||
.store(in: &cancellables)
|
||||
|
||||
viewModel.navigations
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] in self?.handle(navigation: $0) }
|
||||
.store(in: &cancellables)
|
||||
|
||||
|
@ -78,6 +79,30 @@ extension MainNavigationViewController: UITabBarControllerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension MainNavigationViewController: NavigationHandling {
|
||||
func handle(navigation: Navigation) {
|
||||
switch navigation {
|
||||
case .notification:
|
||||
let index = NavigationViewModel.Tab.notifications.rawValue
|
||||
|
||||
guard let viewControllers = viewControllers,
|
||||
viewControllers.count > index,
|
||||
let notificationsNavigationController = viewControllers[index] as? UINavigationController,
|
||||
let notificationsViewController =
|
||||
notificationsNavigationController.viewControllers.first as? NotificationsViewController
|
||||
else { break }
|
||||
|
||||
selectedIndex = index
|
||||
notificationsNavigationController.popToRootViewController(animated: false)
|
||||
notificationsViewController.handle(navigation: navigation)
|
||||
default:
|
||||
((selectedViewController as? UINavigationController)?
|
||||
.topViewController as? NavigationHandling)?
|
||||
.handle(navigation: navigation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension MainNavigationViewController {
|
||||
static let secondaryNavigationViewTag = UUID().hashValue
|
||||
static let newStatusViewTag = UUID().hashValue
|
||||
|
@ -211,42 +236,4 @@ private extension MainNavigationViewController {
|
|||
dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
func handle(navigation: Navigation) {
|
||||
switch navigation {
|
||||
case let .collection(collectionService):
|
||||
let vc = TableViewController(
|
||||
viewModel: CollectionItemsViewModel(
|
||||
collectionService: collectionService,
|
||||
identityContext: viewModel.identityContext),
|
||||
rootViewModel: rootViewModel)
|
||||
|
||||
selectedViewController?.show(vc, sender: self)
|
||||
case let .profile(profileService):
|
||||
let vc = ProfileViewController(
|
||||
viewModel: ProfileViewModel(
|
||||
profileService: profileService,
|
||||
identityContext: viewModel.identityContext),
|
||||
rootViewModel: rootViewModel,
|
||||
identityContext: viewModel.identityContext,
|
||||
parentNavigationController: nil)
|
||||
|
||||
selectedViewController?.show(vc, sender: self)
|
||||
case .notification:
|
||||
let index = NavigationViewModel.Tab.notifications.rawValue
|
||||
|
||||
guard let viewControllers = viewControllers,
|
||||
viewControllers.count > index,
|
||||
let notificationsNavigationController = viewControllers[index] as? UINavigationController,
|
||||
let notificationsViewController =
|
||||
notificationsNavigationController.viewControllers.first as? NotificationsViewController
|
||||
else { break }
|
||||
|
||||
selectedIndex = index
|
||||
notificationsNavigationController.popToRootViewController(animated: false)
|
||||
notificationsViewController.handle(navigation: navigation)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ final class NotificationsViewController: UIPageViewController {
|
|||
}
|
||||
}
|
||||
|
||||
extension NotificationsViewController {
|
||||
extension NotificationsViewController: NavigationHandling {
|
||||
func handle(navigation: Navigation) {
|
||||
switch navigation {
|
||||
case .notification:
|
||||
|
@ -81,7 +81,7 @@ extension NotificationsViewController {
|
|||
setViewControllers([firstViewController], direction: .reverse, animated: false)
|
||||
firstViewController.handle(navigation: navigation)
|
||||
default:
|
||||
break
|
||||
(viewControllers?.first as? TableViewController)?.handle(navigation: navigation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import AVKit
|
||||
import Combine
|
||||
import Mastodon
|
||||
import SafariServices
|
||||
import SDWebImage
|
||||
import SwiftUI
|
||||
import ViewModels
|
||||
|
@ -257,7 +256,9 @@ extension TableViewController {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TableViewController: NavigationHandling {
|
||||
func handle(navigation: Navigation) {
|
||||
switch navigation {
|
||||
case let .collection(collectionService):
|
||||
|
@ -273,6 +274,8 @@ extension TableViewController {
|
|||
} else {
|
||||
show(vc, sender: self)
|
||||
}
|
||||
|
||||
webfingerIndicatorView.stopAnimating()
|
||||
case let .profile(profileService):
|
||||
let vc = ProfileViewController(
|
||||
viewModel: ProfileViewModel(
|
||||
|
@ -287,10 +290,13 @@ extension TableViewController {
|
|||
} else {
|
||||
show(vc, sender: self)
|
||||
}
|
||||
|
||||
webfingerIndicatorView.stopAnimating()
|
||||
case let .notification(notificationService):
|
||||
navigate(toNotification: notificationService.notification)
|
||||
case let .url(url):
|
||||
open(url: url)
|
||||
open(url: url, identityContext: viewModel.identityContext)
|
||||
webfingerIndicatorView.stopAnimating()
|
||||
case .searchScope:
|
||||
break
|
||||
case .webfingerStart:
|
||||
|
@ -562,26 +568,6 @@ private extension TableViewController {
|
|||
viewModel.select(indexPath: indexPath)
|
||||
}
|
||||
|
||||
func open(url: URL) {
|
||||
func openWithRegardToBrowserSetting(url: URL) {
|
||||
if viewModel.identityContext.appPreferences.openLinksInDefaultBrowser || !url.isHTTPURL {
|
||||
UIApplication.shared.open(url)
|
||||
} else {
|
||||
present(SFSafariViewController(url: url), animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
if viewModel.identityContext.appPreferences.useUniversalLinks {
|
||||
UIApplication.shared.open(url, options: [.universalLinksOnly: true]) { success in
|
||||
if !success {
|
||||
openWithRegardToBrowserSetting(url: url)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
openWithRegardToBrowserSetting(url: url)
|
||||
}
|
||||
}
|
||||
|
||||
func present(attachmentViewModel: AttachmentViewModel, statusViewModel: StatusViewModel) {
|
||||
switch attachmentViewModel.attachment.type {
|
||||
case .audio, .video:
|
||||
|
|
|
@ -113,3 +113,9 @@ extension TimelinesViewController: ScrollableToTop {
|
|||
(viewControllers?.first as? TableViewController)?.scrollToTop(animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
extension TimelinesViewController: NavigationHandling {
|
||||
func handle(navigation: Navigation) {
|
||||
(viewControllers?.first as? TableViewController)?.handle(navigation: navigation)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,6 +130,14 @@ public extension NavigationViewModel {
|
|||
titleComponents: ["preferences.blocked-users"])))
|
||||
}
|
||||
|
||||
func navigateToOfficialAccount() {
|
||||
presentingSecondaryNavigation = false
|
||||
presentedNewStatusViewModel = nil
|
||||
identityContext.service.navigationService.item(url: Self.officialAccountURL)
|
||||
.sink { [weak self] in self?.navigationsSubject.send($0) }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func navigate(pushNotification: PushNotification) {
|
||||
switch pushNotification.notificationType {
|
||||
case .followRequest:
|
||||
|
@ -184,3 +192,7 @@ public extension NavigationViewModel {
|
|||
return conversationsViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private extension NavigationViewModel {
|
||||
static let officialAccountURL = URL(string: "https://mastodon.social/@metabolist")!
|
||||
}
|
||||
|
|
8
Views/NavigationHandling.swift
Normal file
8
Views/NavigationHandling.swift
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Copyright © 2021 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import ViewModels
|
||||
|
||||
protocol NavigationHandling {
|
||||
func handle(navigation: Navigation)
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
// Copyright © 2021 Metabolist. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
import ViewModels
|
||||
|
||||
struct AboutView: View {
|
||||
@StateObject var viewModel: NavigationViewModel
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
|
@ -14,6 +17,24 @@ struct AboutView: View {
|
|||
.padding()
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
Section(header: Text("about.made-by-metabolist")) {
|
||||
Button {
|
||||
viewModel.navigateToOfficialAccount()
|
||||
} label: {
|
||||
Label {
|
||||
Text("about.official-account").foregroundColor(.primary)
|
||||
} icon: {
|
||||
Image(systemName: "checkmark.seal")
|
||||
}
|
||||
}
|
||||
Link(destination: Self.websiteURL) {
|
||||
Label {
|
||||
Text("about.website").foregroundColor(.primary)
|
||||
} icon: {
|
||||
Image(systemName: "link")
|
||||
}
|
||||
}
|
||||
}
|
||||
Section {
|
||||
NavigationLink(
|
||||
destination: AcknowledgmentsView()) {
|
||||
|
@ -26,6 +47,7 @@ struct AboutView: View {
|
|||
}
|
||||
|
||||
private extension AboutView {
|
||||
static let websiteURL = URL(string: "https://metabolist.org")!
|
||||
static var version: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
|
||||
}
|
||||
|
@ -40,7 +62,7 @@ import PreviewViewModels
|
|||
|
||||
struct AboutView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AboutView()
|
||||
AboutView(viewModel: NavigationViewModel(identityContext: .preview))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -70,7 +70,7 @@ struct SecondaryNavigationView: View {
|
|||
Label("secondary-navigation.preferences", systemImage: "gear")
|
||||
}
|
||||
NavigationLink(
|
||||
destination: AboutView()
|
||||
destination: AboutView(viewModel: viewModel)
|
||||
.environmentObject(rootViewModel)) {
|
||||
Label("secondary-navigation.about", systemImage: "info.circle")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue