Refactor: Remove NeedsDependency

The AppContext is already a singleton. SceneCoordinators are unique to UIWindowScenes, so fetch them that way.

Fixes iOS-324
This commit is contained in:
shannon 2024-12-02 14:45:15 -05:00
parent fc7ebd65f8
commit 65bf555d98
142 changed files with 401 additions and 757 deletions

View File

@ -397,7 +397,6 @@
DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */; }; DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */; };
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */; }; DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */; };
DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54225C13647002E6C99 /* SceneCoordinator.swift */; }; DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54225C13647002E6C99 /* SceneCoordinator.swift */; };
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54325C13647002E6C99 /* NeedsDependency.swift */; };
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */; }; DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */; };
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; }; DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; };
DB8F7076279E954700E1225B /* DataSourceFacade+Follow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8F7075279E954700E1225B /* DataSourceFacade+Follow.swift */; }; DB8F7076279E954700E1225B /* DataSourceFacade+Follow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8F7075279E954700E1225B /* DataSourceFacade+Follow.swift */; };
@ -1100,7 +1099,6 @@
DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollOptionAppendEntryCollectionViewCell.swift; sourceTree = "<group>"; }; DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollOptionAppendEntryCollectionViewCell.swift; sourceTree = "<group>"; };
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Mastodon.entitlements; sourceTree = "<group>"; }; DB89BA1025C10FF5008580ED /* Mastodon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Mastodon.entitlements; sourceTree = "<group>"; };
DB8AF54225C13647002E6C99 /* SceneCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneCoordinator.swift; sourceTree = "<group>"; }; DB8AF54225C13647002E6C99 /* SceneCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneCoordinator.swift; sourceTree = "<group>"; };
DB8AF54325C13647002E6C99 /* NeedsDependency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeedsDependency.swift; sourceTree = "<group>"; };
DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = "<group>"; }; DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = "<group>"; };
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; }; DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
DB8D8E3128196FA0009FD90F /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = "<group>"; }; DB8D8E3128196FA0009FD90F /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = "<group>"; };
@ -2588,7 +2586,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DB8AF54225C13647002E6C99 /* SceneCoordinator.swift */, DB8AF54225C13647002E6C99 /* SceneCoordinator.swift */,
DB8AF54325C13647002E6C99 /* NeedsDependency.swift */,
D8F9170E2A4B47EF008A5370 /* Coordinator.swift */, D8F9170E2A4B47EF008A5370 /* Coordinator.swift */,
); );
path = Coordinator; path = Coordinator;
@ -3812,7 +3809,6 @@
2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */, 2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */,
DBEFCD7B282A162400C0ABEA /* ReportReasonView.swift in Sources */, DBEFCD7B282A162400C0ABEA /* ReportReasonView.swift in Sources */,
D8E5C346296DAB84007E76A7 /* DataSourceFacade+Status+History.swift in Sources */, D8E5C346296DAB84007E76A7 /* DataSourceFacade+Status+History.swift in Sources */,
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
DB63F77B279ACAE500455B82 /* DataSourceFacade+Favorite.swift in Sources */, DB63F77B279ACAE500455B82 /* DataSourceFacade+Favorite.swift in Sources */,
DB9D6BF825E4F5690051B173 /* NotificationViewController.swift in Sources */, DB9D6BF825E4F5690051B173 /* NotificationViewController.swift in Sources */,
2DAC9E46262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift in Sources */, 2DAC9E46262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift in Sources */,

View File

@ -15,7 +15,7 @@ final class SafariActivity: UIActivity {
weak var sceneCoordinator: SceneCoordinator? weak var sceneCoordinator: SceneCoordinator?
var url: NSURL? var url: NSURL?
init(sceneCoordinator: SceneCoordinator) { init(sceneCoordinator: SceneCoordinator?) {
self.sceneCoordinator = sceneCoordinator self.sceneCoordinator = sceneCoordinator
} }

View File

@ -1,19 +0,0 @@
//
// NeedsDependency.swift
// Mastodon
//
// Created by Cirno MainasuK on 2021-1-27.
//
import UIKit
import MastodonCore
protocol NeedsDependency: AnyObject {
//FIXME: Get rid of ! ~@zeitschlag
var context: AppContext! { get set }
var coordinator: SceneCoordinator! { get set }
}
typealias ViewControllerWithDependencies = NeedsDependency & UIViewController

View File

@ -17,11 +17,14 @@ import MBProgressHUD
@MainActor @MainActor
final public class SceneCoordinator { final public class SceneCoordinator {
fileprivate static func coordinator(for view: UIView) -> SceneCoordinator? {
return SceneDelegate.delegate(for: view)?.coordinator
}
private var disposeBag = Set<AnyCancellable>() private var disposeBag = Set<AnyCancellable>()
private weak var scene: UIScene! private weak var scene: UIScene!
private weak var sceneDelegate: SceneDelegate! private weak var sceneDelegate: SceneDelegate!
private(set) weak var appContext: AppContext!
var authenticationBox: MastodonAuthenticationBox? { var authenticationBox: MastodonAuthenticationBox? {
AuthenticationServiceProvider.shared.currentActiveUser.value AuthenticationServiceProvider.shared.currentActiveUser.value
@ -45,7 +48,6 @@ final public class SceneCoordinator {
) { ) {
self.scene = scene self.scene = scene
self.sceneDelegate = sceneDelegate self.sceneDelegate = sceneDelegate
self.appContext = appContext
NotificationService.shared.requestRevealNotificationPublisher NotificationService.shared.requestRevealNotificationPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
@ -118,7 +120,6 @@ final public class SceneCoordinator {
break break
case .mention, .reblog, .favourite, .poll, .status: case .mention, .reblog, .favourite, .poll, .status:
let threadViewModel = RemoteThreadViewModel( let threadViewModel = RemoteThreadViewModel(
context: appContext,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
notificationID: notificationID notificationID: notificationID
) )
@ -250,17 +251,18 @@ extension SceneCoordinator {
switch UIDevice.current.userInterfaceIdiom { switch UIDevice.current.userInterfaceIdiom {
case .phone: case .phone:
let viewController = MainTabBarController(context: appContext, coordinator: self, authenticationBox: authenticationBox) let viewController = MainTabBarController(authenticationBox: authenticationBox)
self.splitViewController = nil self.splitViewController = nil
self.tabBarController = viewController self.tabBarController = viewController
rootViewController = viewController rootViewController = viewController
default: default:
let splitViewController = RootSplitViewController(context: appContext, coordinator: self, authenticationBox: authenticationBox) let splitViewController = RootSplitViewController(authenticationBox: authenticationBox)
self.splitViewController = splitViewController self.splitViewController = splitViewController
self.tabBarController = splitViewController.contentSplitViewController.mainTabBarController self.tabBarController = splitViewController.contentSplitViewController.mainTabBarController
rootViewController = splitViewController rootViewController = splitViewController
} }
// this feels wrong
sceneDelegate.window?.rootViewController = rootViewController // base: main sceneDelegate.window?.rootViewController = rootViewController // base: main
self.rootViewController = rootViewController self.rootViewController = rootViewController
@ -268,7 +270,7 @@ extension SceneCoordinator {
DispatchQueue.main.async { DispatchQueue.main.async {
_ = self.present( _ = self.present(
scene: .welcome, scene: .welcome,
from: self.sceneDelegate.window?.rootViewController, from: rootViewController, // self.sceneDelegate.window?.rootViewController,
transition: .modal(animated: true, completion: nil) transition: .modal(animated: true, completion: nil)
) )
} }
@ -422,13 +424,11 @@ private extension SceneCoordinator {
let _viewController = WebViewController(viewModel) let _viewController = WebViewController(viewModel)
viewController = _viewController viewController = _viewController
case .searchDetail(let viewModel): case .searchDetail(let viewModel):
let _viewController = SearchDetailViewController(appContext: appContext, sceneCoordinator: self, authenticationBox: viewModel.authenticationBox) let _viewController = SearchDetailViewController(authenticationBox: viewModel.authenticationBox)
_viewController.viewModel = viewModel _viewController.viewModel = viewModel
viewController = _viewController viewController = _viewController
case .searchResult(let viewModel): case .searchResult(let viewModel):
let searchResultViewController = SearchResultViewController() let searchResultViewController = SearchResultViewController()
searchResultViewController.context = appContext
searchResultViewController.coordinator = self
searchResultViewController.viewModel = viewModel searchResultViewController.viewModel = viewModel
viewController = searchResultViewController viewController = searchResultViewController
case .compose(let viewModel): case .compose(let viewModel):
@ -460,19 +460,19 @@ private extension SceneCoordinator {
case .followedTags(let viewModel): case .followedTags(let viewModel):
guard let authenticationBox else { return nil } guard let authenticationBox else { return nil }
viewController = FollowedTagsViewController(appContext: appContext, sceneCoordinator: self, authenticationBox: authenticationBox, viewModel: viewModel) viewController = FollowedTagsViewController(authenticationBox: authenticationBox, viewModel: viewModel)
case .favorite(let viewModel): case .favorite(let viewModel):
let _viewController = FavoriteViewController() let _viewController = FavoriteViewController()
_viewController.viewModel = viewModel _viewController.viewModel = viewModel
viewController = _viewController viewController = _viewController
case .follower(let viewModel): case .follower(let viewModel):
let followerListViewController = FollowerListViewController(viewModel: viewModel, coordinator: self, context: appContext) let followerListViewController = FollowerListViewController(viewModel: viewModel)
viewController = followerListViewController viewController = followerListViewController
case .following(let viewModel): case .following(let viewModel):
let followingListViewController = FollowingListViewController(viewModel: viewModel, coordinator: self, context: appContext) let followingListViewController = FollowingListViewController(viewModel: viewModel)
viewController = followingListViewController viewController = followingListViewController
case .familiarFollowers(let viewModel): case .familiarFollowers(let viewModel):
viewController = FamiliarFollowersViewController(viewModel: viewModel, context: appContext, coordinator: self) viewController = FamiliarFollowersViewController(viewModel: viewModel)
case .rebloggedBy(let viewModel): case .rebloggedBy(let viewModel):
let _viewController = RebloggedByViewController() let _viewController = RebloggedByViewController()
_viewController.viewModel = viewModel _viewController.viewModel = viewModel
@ -539,7 +539,7 @@ private extension SceneCoordinator {
let settingsCoordinator = SettingsCoordinator(presentedOn: presentedOn, let settingsCoordinator = SettingsCoordinator(presentedOn: presentedOn,
accountName: accountName, accountName: accountName,
setting: setting, setting: setting,
appContext: appContext, appContext: AppContext.shared,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
sceneCoordinator: self sceneCoordinator: self
) )
@ -557,18 +557,11 @@ private extension SceneCoordinator {
case .notificationPolicy(let viewModel): case .notificationPolicy(let viewModel):
viewController = NotificationPolicyViewController(viewModel: viewModel) viewController = NotificationPolicyViewController(viewModel: viewModel)
case .accountNotificationTimeline(let viewModel, let request): case .accountNotificationTimeline(let viewModel, let request):
viewController = AccountNotificationTimelineViewController(viewModel: viewModel, context: appContext, coordinator: self, notificationRequest: request) viewController = AccountNotificationTimelineViewController(viewModel: viewModel, notificationRequest: request)
} }
setupDependency(for: viewController as? NeedsDependency)
return viewController return viewController
} }
private func setupDependency(for needs: NeedsDependency?) {
needs?.context = appContext
needs?.coordinator = self
}
} }
//MARK: - Loading //MARK: - Loading
@ -692,3 +685,10 @@ extension SceneCoordinator: SettingsCoordinatorDelegate {
self.mastodonAuthenticationController = authenticationController self.mastodonAuthenticationController = authenticationController
} }
} }
public extension UIViewController {
var sceneCoordinator: SceneCoordinator? {
guard let view = viewIfLoaded else { assert(false); return nil }
return SceneCoordinator.coordinator(for: view)
}
}

View File

@ -47,7 +47,6 @@ extension ReportSection {
case .status(let status): case .status(let status):
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ReportStatusTableViewCell.self), for: indexPath) as! ReportStatusTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ReportStatusTableViewCell.self), for: indexPath) as! ReportStatusTableViewCell
configure( configure(
context: context,
tableView: tableView, tableView: tableView,
cell: cell, cell: cell,
viewModel: .init(value: status), viewModel: .init(value: status),
@ -83,19 +82,16 @@ extension ReportSection {
extension ReportSection { extension ReportSection {
static func configure( static func configure(
context: AppContext,
tableView: UITableView, tableView: UITableView,
cell: ReportStatusTableViewCell, cell: ReportStatusTableViewCell,
viewModel: ReportStatusTableViewCell.ViewModel, viewModel: ReportStatusTableViewCell.ViewModel,
configuration: Configuration configuration: Configuration
) { ) {
StatusSection.setupStatusPollDataSource( StatusSection.setupStatusPollDataSource(
context: context,
authenticationBox: configuration.authenticationBox, authenticationBox: configuration.authenticationBox,
statusView: cell.statusView statusView: cell.statusView
) )
cell.statusView.viewModel.context = context
cell.statusView.viewModel.authenticationBox = configuration.authenticationBox cell.statusView.viewModel.authenticationBox = configuration.authenticationBox
cell.configure( cell.configure(

View File

@ -24,7 +24,6 @@ enum StatusSection: Equatable, Hashable {
extension StatusSection { extension StatusSection {
struct Configuration { struct Configuration {
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
weak var statusTableViewCellDelegate: StatusTableViewCellDelegate? weak var statusTableViewCellDelegate: StatusTableViewCellDelegate?
weak var timelineMiddleLoaderTableViewCellDelegate: TimelineMiddleLoaderTableViewCellDelegate? weak var timelineMiddleLoaderTableViewCellDelegate: TimelineMiddleLoaderTableViewCellDelegate?
@ -33,7 +32,6 @@ extension StatusSection {
static func diffableDataSource( static func diffableDataSource(
tableView: UITableView, tableView: UITableView,
context: AppContext,
configuration: Configuration configuration: Configuration
) -> UITableViewDiffableDataSource<StatusSection, StatusItem> { ) -> UITableViewDiffableDataSource<StatusSection, StatusItem> {
tableView.register(StatusTableViewCell.self, forCellReuseIdentifier: String(describing: StatusTableViewCell.self)) tableView.register(StatusTableViewCell.self, forCellReuseIdentifier: String(describing: StatusTableViewCell.self))
@ -48,7 +46,6 @@ extension StatusSection {
let displayItem = StatusTableViewCell.StatusTableViewCellViewModel.DisplayItem.feed(feed) let displayItem = StatusTableViewCell.StatusTableViewCellViewModel.DisplayItem.feed(feed)
let contentConcealModel = StatusView.ContentConcealViewModel(status: feed.status, filterBox: StatusFilterService.shared.activeFilterBox, filterContext: configuration.filterContext) let contentConcealModel = StatusView.ContentConcealViewModel(status: feed.status, filterBox: StatusFilterService.shared.activeFilterBox, filterContext: configuration.filterContext)
configure( configure(
context: context,
tableView: tableView, tableView: tableView,
cell: cell, cell: cell,
viewModel: StatusTableViewCell.StatusTableViewCellViewModel(displayItem: displayItem, contentConcealModel: contentConcealModel), viewModel: StatusTableViewCell.StatusTableViewCellViewModel(displayItem: displayItem, contentConcealModel: contentConcealModel),
@ -68,7 +65,6 @@ extension StatusSection {
let displayItem = StatusTableViewCell.StatusTableViewCellViewModel.DisplayItem.status(status) let displayItem = StatusTableViewCell.StatusTableViewCellViewModel.DisplayItem.status(status)
let contentConcealModel = StatusView.ContentConcealViewModel(status: status, filterBox: StatusFilterService.shared.activeFilterBox, filterContext: configuration.filterContext) let contentConcealModel = StatusView.ContentConcealViewModel(status: status, filterBox: StatusFilterService.shared.activeFilterBox, filterContext: configuration.filterContext)
configure( configure(
context: context,
tableView: tableView, tableView: tableView,
cell: cell, cell: cell,
viewModel: StatusTableViewCell.StatusTableViewCellViewModel(displayItem: displayItem, contentConcealModel: contentConcealModel), viewModel: StatusTableViewCell.StatusTableViewCellViewModel(displayItem: displayItem, contentConcealModel: contentConcealModel),
@ -77,7 +73,6 @@ extension StatusSection {
return cell return cell
case .thread(let thread): case .thread(let thread):
let cell = dequeueConfiguredReusableCell( let cell = dequeueConfiguredReusableCell(
context: context,
tableView: tableView, tableView: tableView,
indexPath: indexPath, indexPath: indexPath,
configuration: ThreadCellRegistrationConfiguration( configuration: ThreadCellRegistrationConfiguration(
@ -108,7 +103,6 @@ extension StatusSection {
} }
static func dequeueConfiguredReusableCell( static func dequeueConfiguredReusableCell(
context: AppContext,
tableView: UITableView, tableView: UITableView,
indexPath: IndexPath, indexPath: IndexPath,
configuration: ThreadCellRegistrationConfiguration configuration: ThreadCellRegistrationConfiguration
@ -118,7 +112,6 @@ extension StatusSection {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StatusThreadRootTableViewCell.self), for: indexPath) as! StatusThreadRootTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StatusThreadRootTableViewCell.self), for: indexPath) as! StatusThreadRootTableViewCell
let contentConcealModel = StatusView.ContentConcealViewModel(status: threadContext.status, filterBox: StatusFilterService.shared.activeFilterBox, filterContext: .thread) let contentConcealModel = StatusView.ContentConcealViewModel(status: threadContext.status, filterBox: StatusFilterService.shared.activeFilterBox, filterContext: .thread)
StatusSection.configure( StatusSection.configure(
context: context,
tableView: tableView, tableView: tableView,
cell: cell, cell: cell,
viewModel: StatusTableViewCell.StatusTableViewCellViewModel(displayItem: .status(threadContext.status), contentConcealModel: contentConcealModel), viewModel: StatusTableViewCell.StatusTableViewCellViewModel(displayItem: .status(threadContext.status), contentConcealModel: contentConcealModel),
@ -132,9 +125,7 @@ extension StatusSection {
let contentConcealModel = StatusView.ContentConcealViewModel(status: threadContext.status, filterBox: StatusFilterService.shared.activeFilterBox, filterContext: configuration.configuration.filterContext) let contentConcealModel = StatusView.ContentConcealViewModel(status: threadContext.status, filterBox: StatusFilterService.shared.activeFilterBox, filterContext: configuration.configuration.filterContext)
assert(configuration.configuration.filterContext == .thread) assert(configuration.configuration.filterContext == .thread)
StatusSection.configure( StatusSection.configure(
context: context, tableView: tableView, cell: cell,
tableView: tableView,
cell: cell,
viewModel: StatusTableViewCell.StatusTableViewCellViewModel(displayItem: displayItem, contentConcealModel: contentConcealModel), viewModel: StatusTableViewCell.StatusTableViewCellViewModel(displayItem: displayItem, contentConcealModel: contentConcealModel),
configuration: configuration.configuration configuration: configuration.configuration
) )
@ -147,7 +138,6 @@ extension StatusSection {
extension StatusSection { extension StatusSection {
public static func setupStatusPollDataSource( public static func setupStatusPollDataSource(
context: AppContext,
authenticationBox: MastodonAuthenticationBox, authenticationBox: MastodonAuthenticationBox,
statusView: StatusView statusView: StatusView
) { ) {
@ -206,19 +196,16 @@ extension StatusSection {
extension StatusSection { extension StatusSection {
static func configure( static func configure(
context: AppContext,
tableView: UITableView, tableView: UITableView,
cell: StatusTableViewCell, cell: StatusTableViewCell,
viewModel: StatusTableViewCell.StatusTableViewCellViewModel, viewModel: StatusTableViewCell.StatusTableViewCellViewModel,
configuration: Configuration configuration: Configuration
) { ) {
setupStatusPollDataSource( setupStatusPollDataSource(
context: context,
authenticationBox: configuration.authenticationBox, authenticationBox: configuration.authenticationBox,
statusView: cell.statusView statusView: cell.statusView
) )
cell.statusView.viewModel.context = configuration.context
cell.statusView.viewModel.authenticationBox = configuration.authenticationBox cell.statusView.viewModel.authenticationBox = configuration.authenticationBox
cell.configure( cell.configure(
@ -229,19 +216,16 @@ extension StatusSection {
} }
static func configure( static func configure(
context: AppContext,
tableView: UITableView, tableView: UITableView,
cell: StatusThreadRootTableViewCell, cell: StatusThreadRootTableViewCell,
viewModel: StatusTableViewCell.StatusTableViewCellViewModel, viewModel: StatusTableViewCell.StatusTableViewCellViewModel,
configuration: Configuration configuration: Configuration
) { ) {
setupStatusPollDataSource( setupStatusPollDataSource(
context: context,
authenticationBox: configuration.authenticationBox, authenticationBox: configuration.authenticationBox,
statusView: cell.statusView statusView: cell.statusView
) )
cell.statusView.viewModel.context = configuration.context
cell.statusView.viewModel.authenticationBox = configuration.authenticationBox cell.statusView.viewModel.authenticationBox = configuration.authenticationBox
cell.configure( cell.configure(

View File

@ -21,7 +21,6 @@ enum UserSection: Hashable {
extension UserSection { extension UserSection {
static func diffableDataSource( static func diffableDataSource(
tableView: UITableView, tableView: UITableView,
context: AppContext,
authenticationBox: MastodonAuthenticationBox, authenticationBox: MastodonAuthenticationBox,
userTableViewCellDelegate: UserTableViewCellDelegate? userTableViewCellDelegate: UserTableViewCellDelegate?
) -> UITableViewDiffableDataSource<UserSection, UserItem> { ) -> UITableViewDiffableDataSource<UserSection, UserItem> {

View File

@ -12,7 +12,7 @@ import MastodonSDK
extension DataSourceFacade { extension DataSourceFacade {
static func responseToUserBlockAction( static func responseToUserBlockAction(
dependency: NeedsDependency & AuthContextProvider, dependency: AuthContextProvider,
account: Mastodon.Entity.Account account: Mastodon.Entity.Account
) async throws -> Mastodon.Entity.Relationship { ) async throws -> Mastodon.Entity.Relationship {
FeedbackGenerator.shared.generate(.selectionChanged) FeedbackGenerator.shared.generate(.selectionChanged)
@ -35,7 +35,7 @@ extension DataSourceFacade {
} }
static func responseToDomainBlockAction( static func responseToDomainBlockAction(
dependency: NeedsDependency & AuthContextProvider, dependency: AuthContextProvider,
account: Mastodon.Entity.Account account: Mastodon.Entity.Account
) async throws -> Mastodon.Entity.Empty { ) async throws -> Mastodon.Entity.Empty {
FeedbackGenerator.shared.generate(.selectionChanged) FeedbackGenerator.shared.generate(.selectionChanged)

View File

@ -14,7 +14,7 @@ import MastodonSDK
extension DataSourceFacade { extension DataSourceFacade {
@MainActor @MainActor
public static func responseToStatusBookmarkAction( public static func responseToStatusBookmarkAction(
provider: NeedsDependency & AuthContextProvider & DataSourceProvider, provider: AuthContextProvider & DataSourceProvider,
status: MastodonStatus status: MastodonStatus
) async throws { ) async throws {
FeedbackGenerator.shared.generate(.selectionChanged) FeedbackGenerator.shared.generate(.selectionChanged)

View File

@ -14,7 +14,7 @@ import MastodonLocalization
extension DataSourceFacade { extension DataSourceFacade {
@MainActor @MainActor
static func responseToUserFollowAction( static func responseToUserFollowAction(
dependency: ViewControllerWithDependencies & AuthContextProvider, dependency: UIViewController & AuthContextProvider,
account: Mastodon.Entity.Account account: Mastodon.Entity.Account
) async throws -> Mastodon.Entity.Relationship { ) async throws -> Mastodon.Entity.Relationship {
let authBox = dependency.authenticationBox let authBox = dependency.authenticationBox
@ -78,7 +78,7 @@ extension DataSourceFacade {
extension DataSourceFacade { extension DataSourceFacade {
static func responseToUserFollowRequestAction( static func responseToUserFollowRequestAction(
dependency: NeedsDependency & AuthContextProvider, dependency: UIViewController & AuthContextProvider,
notification: MastodonNotification, notification: MastodonNotification,
notificationView: NotificationView, notificationView: NotificationView,
query: Mastodon.API.Account.FollowRequestQuery query: Mastodon.API.Account.FollowRequestQuery
@ -129,10 +129,11 @@ extension DataSourceFacade {
case .notFound: case .notFound:
break break
default: default:
guard let coordinator = await dependency.sceneCoordinator else { return }
let alertController = await UIAlertController(for: error, title: nil, preferredStyle: .alert) let alertController = await UIAlertController(for: error, title: nil, preferredStyle: .alert)
let okAction = await UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) let okAction = await UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default)
await alertController.addAction(okAction) await alertController.addAction(okAction)
_ = await dependency.coordinator.present( _ = await coordinator.present(
scene: .alertController(alertController: alertController), scene: .alertController(alertController: alertController),
from: nil, from: nil,
transition: .alertController(animated: true, completion: nil) transition: .alertController(animated: true, completion: nil)
@ -146,7 +147,7 @@ extension DataSourceFacade {
extension DataSourceFacade { extension DataSourceFacade {
static func responseToShowHideReblogAction( static func responseToShowHideReblogAction(
dependency: NeedsDependency & AuthContextProvider, dependency: AuthContextProvider,
account: Mastodon.Entity.Account account: Mastodon.Entity.Account
) async throws { ) async throws {
let newRelationship = try await APIService.shared.toggleShowReblogs( let newRelationship = try await APIService.shared.toggleShowReblogs(

View File

@ -13,16 +13,16 @@ import MastodonSDK
extension DataSourceFacade { extension DataSourceFacade {
@MainActor @MainActor
static func coordinateToHashtagScene( static func coordinateToHashtagScene(
provider: ViewControllerWithDependencies & AuthContextProvider, provider: UIViewController,
tag: Mastodon.Entity.Tag tag: Mastodon.Entity.Tag
) async { ) async {
guard let authBox = AuthenticationServiceProvider.shared.currentActiveUser.value else { return }
let hashtagTimelineViewModel = HashtagTimelineViewModel( let hashtagTimelineViewModel = HashtagTimelineViewModel(
context: provider.context, authenticationBox: authBox,
authenticationBox: provider.authenticationBox,
hashtag: tag.name hashtag: tag.name
) )
guard let coordinator = provider.sceneCoordinator else { return }
_ = provider.coordinator.present( _ = coordinator.present(
scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
from: provider, from: provider,
transition: .show transition: .show

View File

@ -7,6 +7,7 @@
import UIKit import UIKit
import CoreDataStack import CoreDataStack
import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization import MastodonLocalization
import MastodonSDK import MastodonSDK
@ -15,16 +16,17 @@ extension DataSourceFacade {
@MainActor @MainActor
static func coordinateToMediaPreviewScene( static func coordinateToMediaPreviewScene(
dependency: NeedsDependency & MediaPreviewableViewController, dependency: UIViewController & MediaPreviewableViewController,
mediaPreviewItem: MediaPreviewViewModel.PreviewItem, mediaPreviewItem: MediaPreviewViewModel.PreviewItem,
mediaPreviewTransitionItem: MediaPreviewTransitionItem mediaPreviewTransitionItem: MediaPreviewTransitionItem
) { ) {
let mediaPreviewViewModel = MediaPreviewViewModel( let mediaPreviewViewModel = MediaPreviewViewModel(
context: dependency.context, context: AppContext.shared,
item: mediaPreviewItem, item: mediaPreviewItem,
transitionItem: mediaPreviewTransitionItem transitionItem: mediaPreviewTransitionItem
) )
_ = dependency.coordinator.present( guard let coordinator = dependency.sceneCoordinator else { return }
_ = coordinator.present(
scene: .mediaPreview(viewModel: mediaPreviewViewModel), scene: .mediaPreview(viewModel: mediaPreviewViewModel),
from: dependency, from: dependency,
transition: .custom(transitioningDelegate: dependency.mediaPreviewTransitionController) transition: .custom(transitioningDelegate: dependency.mediaPreviewTransitionController)
@ -61,7 +63,7 @@ extension DataSourceFacade {
@MainActor @MainActor
static func coordinateToMediaPreviewScene( static func coordinateToMediaPreviewScene(
dependency: NeedsDependency & MediaPreviewableViewController, dependency: UIViewController & MediaPreviewableViewController,
status: MastodonStatus, status: MastodonStatus,
previewContext: AttachmentPreviewContext previewContext: AttachmentPreviewContext
) async throws { ) async throws {
@ -146,7 +148,7 @@ extension DataSourceFacade {
@MainActor @MainActor
static func coordinateToMediaPreviewScene( static func coordinateToMediaPreviewScene(
dependency: NeedsDependency & MediaPreviewableViewController, dependency: MediaPreviewableViewController,
account: Mastodon.Entity.Account, account: Mastodon.Entity.Account,
previewContext: ImagePreviewContext previewContext: ImagePreviewContext
) async throws { ) async throws {

View File

@ -53,8 +53,9 @@ extension DataSourceFacade {
url: url url: url
) )
case .hashtag(_, let hashtag, _): case .hashtag(_, let hashtag, _):
let hashtagTimelineViewModel = await HashtagTimelineViewModel(context: provider.context, authenticationBox: provider.authenticationBox, hashtag: hashtag) let hashtagTimelineViewModel = await HashtagTimelineViewModel(authenticationBox: provider.authenticationBox, hashtag: hashtag)
_ = await provider.coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: provider, transition: .show) guard let coordinator = await provider.sceneCoordinator else { return }
_ = await coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: provider, transition: .show)
case .mention(_, let mention, let userInfo): case .mention(_, let mention, let userInfo):
await coordinateToProfileScene( await coordinateToProfileScene(
provider: provider, provider: provider,

View File

@ -11,7 +11,7 @@ import MastodonCore
extension DataSourceFacade { extension DataSourceFacade {
static func responseToUserMuteAction( static func responseToUserMuteAction(
dependency: NeedsDependency & AuthContextProvider, dependency: AuthContextProvider,
account: Mastodon.Entity.Account account: Mastodon.Entity.Account
) async throws -> Mastodon.Entity.Relationship { ) async throws -> Mastodon.Entity.Relationship {
FeedbackGenerator.shared.generate(.selectionChanged) FeedbackGenerator.shared.generate(.selectionChanged)

View File

@ -3,19 +3,21 @@
import Foundation import Foundation
import MastodonCore import MastodonCore
import MastodonSDK import MastodonSDK
import UIKit
extension DataSourceFacade { extension DataSourceFacade {
@MainActor @MainActor
static func coordinateToNotificationRequests( static func coordinateToNotificationRequests(
provider: DataSourceProvider & AuthContextProvider provider: DataSourceProvider & AuthContextProvider
) async { ) async {
provider.coordinator.showLoading() guard let sceneCoordinator = provider.sceneCoordinator else { return }
sceneCoordinator.showLoading()
do { do {
let notificationRequests = try await APIService.shared.notificationRequests(authenticationBox: provider.authenticationBox).value let notificationRequests = try await APIService.shared.notificationRequests(authenticationBox: provider.authenticationBox).value
let viewModel = NotificationRequestsViewModel(appContext: provider.context, authenticationBox: provider.authenticationBox, coordinator: provider.coordinator, requests: notificationRequests) let viewModel = NotificationRequestsViewModel(authenticationBox: provider.authenticationBox, requests: notificationRequests)
provider.coordinator.hideLoading() sceneCoordinator.hideLoading()
let transition: SceneCoordinator.Transition let transition: SceneCoordinator.Transition
@ -25,25 +27,26 @@ extension DataSourceFacade {
transition = .modal(animated: true) transition = .modal(animated: true)
} }
provider.coordinator.present(scene: .notificationRequests(viewModel: viewModel), transition: transition) sceneCoordinator.present(scene: .notificationRequests(viewModel: viewModel), transition: transition)
} catch { } catch {
//TODO: Error Handling //TODO: Error Handling
provider.coordinator.hideLoading() sceneCoordinator.hideLoading()
} }
} }
@MainActor @MainActor
static func coordinateToNotificationRequest( static func coordinateToNotificationRequest(
request: Mastodon.Entity.NotificationRequest, request: Mastodon.Entity.NotificationRequest,
provider: ViewControllerWithDependencies & AuthContextProvider provider: UIViewController & AuthContextProvider
) async -> AccountNotificationTimelineViewController? { ) async -> AccountNotificationTimelineViewController? {
provider.coordinator.showLoading() guard let sceneCoordinator = provider.sceneCoordinator else { return nil }
sceneCoordinator.showLoading()
let notificationTimelineViewModel = NotificationTimelineViewModel(context: provider.context, authenticationBox: provider.authenticationBox, scope: .fromAccount(request.account)) let notificationTimelineViewModel = NotificationTimelineViewModel(authenticationBox: provider.authenticationBox, scope: .fromAccount(request.account))
provider.coordinator.hideLoading() sceneCoordinator.hideLoading()
guard let viewController = provider.coordinator.present(scene: .accountNotificationTimeline(viewModel: notificationTimelineViewModel, request: request), transition: .show) as? AccountNotificationTimelineViewController else { return nil } guard let viewController = sceneCoordinator.present(scene: .accountNotificationTimeline(viewModel: notificationTimelineViewModel, request: request), transition: .show) as? AccountNotificationTimelineViewController else { return nil }
return viewController return viewController

View File

@ -26,7 +26,9 @@ extension DataSourceFacade {
acct = status.entity.account.acct acct = status.entity.account.acct
} }
provider.coordinator.showLoading() guard let coordinator = provider.sceneCoordinator else { return }
coordinator.showLoading()
let _redirectRecord = try? await Mastodon.API.Account.lookupAccount( let _redirectRecord = try? await Mastodon.API.Account.lookupAccount(
session: .shared, session: .shared,
@ -35,7 +37,7 @@ extension DataSourceFacade {
authorization: provider.authenticationBox.userAuthorization authorization: provider.authenticationBox.userAuthorization
).singleOutput().value ).singleOutput().value
provider.coordinator.hideLoading() coordinator.hideLoading()
guard let redirectRecord = _redirectRecord else { guard let redirectRecord = _redirectRecord else {
assertionFailure() assertionFailure()
@ -50,11 +52,13 @@ extension DataSourceFacade {
@MainActor @MainActor
static func coordinateToProfileScene( static func coordinateToProfileScene(
provider: ViewControllerWithDependencies & AuthContextProvider, provider: UIViewController & AuthContextProvider,
username: String, username: String,
domain: String domain: String
) async { ) async {
provider.coordinator.showLoading() guard let coordinator = provider.sceneCoordinator else { return }
coordinator.showLoading()
do { do {
guard let account = try await APIService.shared.fetchUser( guard let account = try await APIService.shared.fetchUser(
@ -62,24 +66,26 @@ extension DataSourceFacade {
domain: domain, domain: domain,
authenticationBox: provider.authenticationBox authenticationBox: provider.authenticationBox
) else { ) else {
return provider.coordinator.hideLoading() return coordinator.hideLoading()
} }
provider.coordinator.hideLoading() coordinator.hideLoading()
await coordinateToProfileScene(provider: provider, account: account) await coordinateToProfileScene(provider: provider, account: account)
} catch { } catch {
provider.coordinator.hideLoading() coordinator.hideLoading()
} }
} }
@MainActor @MainActor
static func coordinateToProfileScene( static func coordinateToProfileScene(
provider: ViewControllerWithDependencies & AuthContextProvider, provider: UIViewController & AuthContextProvider,
domain: String, domain: String,
accountID: String accountID: String
) async { ) async {
provider.coordinator.showLoading() guard let coordinator = provider.sceneCoordinator else { return }
coordinator.showLoading()
do { do {
let account = try await APIService.shared.accountInfo( let account = try await APIService.shared.accountInfo(
@ -89,37 +95,40 @@ extension DataSourceFacade {
authorization: provider.authenticationBox.userAuthorization authorization: provider.authenticationBox.userAuthorization
) )
provider.coordinator.hideLoading() coordinator.hideLoading()
await coordinateToProfileScene(provider: provider, account: account) await coordinateToProfileScene(provider: provider, account: account)
} catch { } catch {
provider.coordinator.hideLoading() coordinator.hideLoading()
} }
} }
@MainActor @MainActor
public static func coordinateToProfileScene( public static func coordinateToProfileScene(
provider: ViewControllerWithDependencies & AuthContextProvider, provider: UIViewController & AuthContextProvider,
account: Mastodon.Entity.Account account: Mastodon.Entity.Account
) async { ) async {
provider.coordinator.showLoading()
guard let coordinator = provider.sceneCoordinator else { return }
coordinator.showLoading()
guard let me = provider.authenticationBox.cachedAccount, guard let me = provider.authenticationBox.cachedAccount,
let relationship = try? await APIService.shared.relationship(forAccounts: [account], authenticationBox: provider.authenticationBox).value.first else { let relationship = try? await APIService.shared.relationship(forAccounts: [account], authenticationBox: provider.authenticationBox).value.first else {
return provider.coordinator.hideLoading() return coordinator.hideLoading()
} }
provider.coordinator.hideLoading() coordinator.hideLoading()
let profileViewModel = ProfileViewModel( let profileViewModel = ProfileViewModel(
context: provider.context, context: AppContext.shared,
authenticationBox: provider.authenticationBox, authenticationBox: provider.authenticationBox,
account: account, account: account,
relationship: relationship, relationship: relationship,
me: me me: me
) )
_ = provider.coordinator.present( _ = coordinator.present(
scene: .profile(viewModel: profileViewModel), scene: .profile(viewModel: profileViewModel),
from: provider, from: provider,
transition: .show transition: .show
@ -146,13 +155,13 @@ extension DataSourceFacade {
} }
let mentions = status.entity.mentions let mentions = status.entity.mentions
guard let coordinator = provider.sceneCoordinator else { return }
guard let mention = mentions.first(where: { $0.url == href }) else { guard let mention = mentions.first(where: { $0.url == href }) else {
_ = provider.coordinator.present( _ = coordinator.present(
scene: .safari(url: url), scene: .safari(url: url),
from: provider, from: provider,
transition: .safariPresent(animated: true, completion: nil) transition: .safariPresent(animated: true, completion: nil)
) )
return return
} }
@ -163,21 +172,21 @@ extension DataSourceFacade {
extension DataSourceFacade { extension DataSourceFacade {
static func createActivityViewController( static func createActivityViewController(
dependency: NeedsDependency, dependency: UIViewController,
account: Mastodon.Entity.Account account: Mastodon.Entity.Account
) -> UIActivityViewController { ) -> UIActivityViewController {
let activityViewController = UIActivityViewController( let activityViewController = UIActivityViewController(
activityItems: [account.url], activityItems: [account.url],
applicationActivities: [SafariActivity(sceneCoordinator: dependency.coordinator)] applicationActivities: [SafariActivity(sceneCoordinator: dependency.sceneCoordinator)]
) )
return activityViewController return activityViewController
} }
static func createActivityViewControllerForMastodonUser(status: Status, dependency: NeedsDependency) -> UIActivityViewController { static func createActivityViewControllerForMastodonUser(status: Status, dependency: UIViewController) -> UIActivityViewController {
let activityViewController = UIActivityViewController( let activityViewController = UIActivityViewController(
activityItems: status.activityItems, activityItems: status.activityItems,
applicationActivities: [SafariActivity(sceneCoordinator: dependency.coordinator)] applicationActivities: [SafariActivity(sceneCoordinator: dependency.sceneCoordinator)]
) )
return activityViewController return activityViewController
} }

View File

@ -13,7 +13,7 @@ import UIKit
extension DataSourceFacade { extension DataSourceFacade {
static func responseToCreateSearchHistory( static func responseToCreateSearchHistory(
provider: ViewControllerWithDependencies & AuthContextProvider, provider: UIViewController & AuthContextProvider,
item: DataSourceItem item: DataSourceItem
) async { ) async {
switch item { switch item {

View File

@ -8,7 +8,7 @@ import CoreDataStack
extension DataSourceFacade { extension DataSourceFacade {
public static func getEditHistory( public static func getEditHistory(
forStatus status: Status, forStatus status: Status,
provider: NeedsDependency & AuthContextProvider provider: AuthContextProvider
) async throws -> [Mastodon.Entity.StatusEdit] { ) async throws -> [Mastodon.Entity.StatusEdit] {
let reponse = try await APIService.shared.getHistory(forStatusID: status.id, authenticationBox: provider.authenticationBox) let reponse = try await APIService.shared.getHistory(forStatusID: status.id, authenticationBox: provider.authenticationBox)

View File

@ -20,7 +20,7 @@ import MastodonSDK
extension DataSourceFacade { extension DataSourceFacade {
static func responseToDeleteStatus( static func responseToDeleteStatus(
dependency: NeedsDependency & AuthContextProvider & DataSourceProvider, dependency: AuthContextProvider & DataSourceProvider,
status: MastodonStatus status: MastodonStatus
) async throws { ) async throws {
let deletedStatus = try await APIService.shared.deleteStatus( let deletedStatus = try await APIService.shared.deleteStatus(
@ -46,7 +46,8 @@ extension DataSourceFacade {
dependency: provider, dependency: provider,
status: status status: status
) )
_ = provider.coordinator.present( guard let coordinator = provider.sceneCoordinator else { /* TODO: throw? */ return }
_ = coordinator.present(
scene: .activityViewController( scene: .activityViewController(
activityViewController: activityViewController, activityViewController: activityViewController,
sourceView: button, sourceView: button,
@ -58,7 +59,7 @@ extension DataSourceFacade {
} }
private static func createActivityViewController( private static func createActivityViewController(
dependency: NeedsDependency, dependency: UIViewController,
status: MastodonStatus status: MastodonStatus
) async throws -> UIActivityViewController { ) async throws -> UIActivityViewController {
var activityItems: [Any] = { var activityItems: [Any] = {
@ -68,8 +69,8 @@ extension DataSourceFacade {
] ]
}() }()
var applicationActivities: [UIActivity] = [ var applicationActivities: [UIActivity] = await [
SafariActivity(sceneCoordinator: dependency.coordinator), // open URL SafariActivity(sceneCoordinator: dependency.sceneCoordinator), // open URL
] ]
if let provider = dependency as? ShareActivityProvider { if let provider = dependency as? ShareActivityProvider {
@ -95,18 +96,19 @@ extension DataSourceFacade {
sender: UIButton sender: UIButton
) async throws { ) async throws {
let _status = status.reblog ?? status let _status = status.reblog ?? status
guard let coordinator = provider.sceneCoordinator else { return }
switch action { switch action {
case .reply: case .reply:
FeedbackGenerator.shared.generate(.selectionChanged) FeedbackGenerator.shared.generate(.selectionChanged)
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: provider.context,
authenticationBox: provider.authenticationBox, authenticationBox: provider.authenticationBox,
composeContext: .composeStatus, composeContext: .composeStatus,
destination: .reply(parent: _status) destination: .reply(parent: _status)
) )
_ = provider.coordinator.present( _ = coordinator.present(
scene: .compose(viewModel: composeViewModel), scene: .compose(viewModel: composeViewModel),
from: provider, from: provider,
transition: .modal(animated: true, completion: nil) transition: .modal(animated: true, completion: nil)
@ -144,7 +146,7 @@ extension DataSourceFacade {
@MainActor @MainActor
static func responseToMenuAction<T>( static func responseToMenuAction<T>(
dependency: UIViewController & NeedsDependency & AuthContextProvider & DataSourceProvider, dependency: AuthContextProvider & DataSourceProvider,
action: MastodonMenu.Action, action: MastodonMenu.Action,
menuContext: MenuContext, menuContext: MenuContext,
completion: ((T) -> Void)? = { (param: Void) in } completion: ((T) -> Void)? = { (param: Void) in }
@ -237,7 +239,7 @@ extension DataSourceFacade {
guard let relationship = try? await APIService.shared.relationship(forAccounts: [menuContext.author], authenticationBox: dependency.authenticationBox).value.first else { return } guard let relationship = try? await APIService.shared.relationship(forAccounts: [menuContext.author], authenticationBox: dependency.authenticationBox).value.first else { return }
let reportViewModel = ReportViewModel( let reportViewModel = ReportViewModel(
context: dependency.context, context: AppContext.shared,
authenticationBox: dependency.authenticationBox, authenticationBox: dependency.authenticationBox,
account: menuContext.author, account: menuContext.author,
relationship: relationship, relationship: relationship,
@ -245,7 +247,8 @@ extension DataSourceFacade {
contentDisplayMode: .neverConceal contentDisplayMode: .neverConceal
) )
_ = dependency.coordinator.present( guard let coordinator = dependency.sceneCoordinator else { return }
_ = coordinator.present(
scene: .report(viewModel: reportViewModel), scene: .report(viewModel: reportViewModel),
from: dependency, from: dependency,
transition: .modal(animated: true, completion: nil) transition: .modal(animated: true, completion: nil)
@ -256,7 +259,8 @@ extension DataSourceFacade {
account: menuContext.author account: menuContext.author
) )
_ = dependency.coordinator.present( guard let coordinator = dependency.sceneCoordinator else { return }
_ = coordinator.present(
scene: .activityViewController( scene: .activityViewController(
activityViewController: activityViewController, activityViewController: activityViewController,
sourceView: menuContext.button, sourceView: menuContext.button,
@ -284,8 +288,8 @@ extension DataSourceFacade {
dependency: dependency, dependency: dependency,
status: status status: status
) )
guard let coordinator = dependency.sceneCoordinator else { return }
_ = dependency.coordinator.present( _ = coordinator.present(
scene: .activityViewController( scene: .activityViewController(
activityViewController: activityViewController, activityViewController: activityViewController,
sourceView: menuContext.button, sourceView: menuContext.button,
@ -321,7 +325,7 @@ extension DataSourceFacade {
guard let status = menuContext.statusViewModel?.originalStatus?.reblog ?? menuContext.statusViewModel?.originalStatus else { return } guard let status = menuContext.statusViewModel?.originalStatus?.reblog ?? menuContext.statusViewModel?.originalStatus else { return }
do { do {
let translation = try await DataSourceFacade.translateStatus(provider: dependency,status: status) let translation = try await DataSourceFacade.translateStatus(provider: dependency, status: status)
menuContext.statusViewModel?.translation = translation menuContext.statusViewModel?.translation = translation
} catch TranslationFailure.emptyOrInvalidResponse { } catch TranslationFailure.emptyOrInvalidResponse {
@ -340,11 +344,11 @@ extension DataSourceFacade {
).value ).value
let editStatusViewModel = ComposeViewModel( let editStatusViewModel = ComposeViewModel(
context: dependency.coordinator.appContext,
authenticationBox: dependency.authenticationBox, authenticationBox: dependency.authenticationBox,
composeContext: .editStatus(status: status, statusSource: statusSource), composeContext: .editStatus(status: status, statusSource: statusSource),
destination: .topLevel) destination: .topLevel)
_ = dependency.coordinator.present(scene: .editStatus(viewModel: editStatusViewModel), transition: .modal(animated: true)) guard let coordinator = dependency.sceneCoordinator else { return }
_ = coordinator.present(scene: .editStatus(viewModel: editStatusViewModel), transition: .modal(animated: true))
case .showOriginal: case .showOriginal:
// do nothing, as the translation is reverted in `StatusTableViewCellDelegate` in `DataSourceProvider+StatusTableViewCellDelegate.swift`. // do nothing, as the translation is reverted in `StatusTableViewCellDelegate` in `DataSourceProvider+StatusTableViewCellDelegate.swift`.
@ -416,14 +420,13 @@ extension DataSourceFacade {
assertionFailure() assertionFailure()
return return
} }
guard let coordinator = dependency.sceneCoordinator else { return }
dependency.coordinator.present(scene: .safari(url: url), transition: .safariPresent(animated: true)) coordinator.present(scene: .safari(url: url), transition: .safariPresent(animated: true))
case .copyProfileLink(let url): case .copyProfileLink(let url):
UIPasteboard.general.string = url?.absoluteString UIPasteboard.general.string = url?.absoluteString
case .openUserInBrowser(let url): case .openUserInBrowser(let url):
guard let url else { return } guard let url, let coordinator = dependency.sceneCoordinator else { return }
coordinator.present(scene: .safari(url: url), transition: .safariPresent(animated: true))
dependency.coordinator.present(scene: .safari(url: url), transition: .safariPresent(animated: true))
} }
} }
} }
@ -431,7 +434,7 @@ extension DataSourceFacade {
extension DataSourceFacade { extension DataSourceFacade {
@MainActor @MainActor
static func responseToToggleSensitiveAction( static func responseToToggleSensitiveAction(
dependency: NeedsDependency & DataSourceProvider, dependency: DataSourceProvider,
status: MastodonStatus status: MastodonStatus
) async throws { ) async throws {
let _status = status.reblog ?? status let _status = status.reblog ?? status
@ -443,7 +446,7 @@ extension DataSourceFacade {
} }
private extension DataSourceFacade { private extension DataSourceFacade {
static func performDeletion(of status: MastodonStatus, with dependency: NeedsDependency & AuthContextProvider & DataSourceProvider) { static func performDeletion(of status: MastodonStatus, with dependency: AuthContextProvider & DataSourceProvider) {
Task { Task {
try await DataSourceFacade.responseToDeleteStatus( try await DataSourceFacade.responseToDeleteStatus(
dependency: dependency, dependency: dependency,

View File

@ -13,7 +13,7 @@ import MastodonSDK
extension DataSourceFacade { extension DataSourceFacade {
static func coordinateToStatusThreadScene( static func coordinateToStatusThreadScene(
provider: ViewControllerWithDependencies & AuthContextProvider, provider: UIViewController,
target: StatusTarget, target: StatusTarget,
status: MastodonStatus status: MastodonStatus
) async { ) async {
@ -39,15 +39,16 @@ extension DataSourceFacade {
@MainActor @MainActor
static func coordinateToStatusThreadScene( static func coordinateToStatusThreadScene(
provider: ViewControllerWithDependencies & AuthContextProvider, provider: UIViewController,
root: StatusItem.Thread root: StatusItem.Thread
) async { ) async {
guard let authBox = AuthenticationServiceProvider.shared.currentActiveUser.value else { return }
let threadViewModel = ThreadViewModel( let threadViewModel = ThreadViewModel(
context: provider.context, authenticationBox: authBox,
authenticationBox: provider.authenticationBox,
optionalRoot: root optionalRoot: root
) )
_ = provider.coordinator.present( guard let coordinator = provider.sceneCoordinator else { return }
_ = coordinator.present(
scene: .thread(viewModel: threadViewModel), scene: .thread(viewModel: threadViewModel),
from: provider, from: provider,
transition: .show transition: .show

View File

@ -11,15 +11,13 @@ import CoreDataStack
import MastodonCore import MastodonCore
import MastodonSDK import MastodonSDK
typealias Provider = UIViewController & NeedsDependency & AuthContextProvider
extension DataSourceFacade { extension DataSourceFacade {
enum TranslationFailure: Error { enum TranslationFailure: Error {
case emptyOrInvalidResponse case emptyOrInvalidResponse
} }
public static func translateStatus( public static func translateStatus(
provider: Provider, provider: AuthContextProvider,
status: MastodonStatus status: MastodonStatus
) async throws -> Mastodon.Entity.Translation { ) async throws -> Mastodon.Entity.Translation {
FeedbackGenerator.shared.generate(.selectionChanged) FeedbackGenerator.shared.generate(.selectionChanged)

View File

@ -16,6 +16,7 @@ extension DataSourceFacade {
provider: DataSourceProvider & AuthContextProvider, provider: DataSourceProvider & AuthContextProvider,
url: URL url: URL
) async { ) async {
guard let coordinator = await provider.sceneCoordinator else { return }
let domain = provider.authenticationBox.domain let domain = provider.authenticationBox.domain
if url.host == domain, if url.host == domain,
url.pathComponents.count >= 4, url.pathComponents.count >= 4,
@ -23,10 +24,10 @@ extension DataSourceFacade {
url.pathComponents[1] == "web", url.pathComponents[1] == "web",
url.pathComponents[2] == "statuses" { url.pathComponents[2] == "statuses" {
let statusID = url.pathComponents[3] let statusID = url.pathComponents[3]
let threadViewModel = await RemoteThreadViewModel(context: provider.context, authenticationBox: provider.authenticationBox, statusID: statusID) let threadViewModel = await RemoteThreadViewModel(authenticationBox: provider.authenticationBox, statusID: statusID)
_ = await provider.coordinator.present(scene: .thread(viewModel: threadViewModel), from: nil, transition: .show) _ = await coordinator.present(scene: .thread(viewModel: threadViewModel), from: nil, transition: .show)
} else { } else {
_ = await provider.coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil)) _ = await coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
} }
} }
} }

View File

@ -5,10 +5,11 @@ import MastodonUI
import CoreDataStack import CoreDataStack
import MastodonCore import MastodonCore
import MastodonSDK import MastodonSDK
import UIKit
extension DataSourceFacade { extension DataSourceFacade {
static func responseToUserViewButtonAction( static func responseToUserViewButtonAction(
dependency: ViewControllerWithDependencies & AuthContextProvider, dependency: UIViewController & AuthContextProvider,
account: Mastodon.Entity.Account, account: Mastodon.Entity.Account,
buttonState: UserView.ButtonState buttonState: UserView.ButtonState
) async throws { ) async throws {

View File

@ -184,7 +184,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
], ],
applicationActivities: [] applicationActivities: []
) )
self.coordinator.present( self.sceneCoordinator?.present(
scene: .activityViewController( scene: .activityViewController(
activityViewController: activityViewController, activityViewController: activityViewController,
sourceView: statusCardControl, barButtonItem: nil sourceView: statusCardControl, barButtonItem: nil
@ -200,9 +200,8 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
image: UIImage(systemName: "square.and.pencil") image: UIImage(systemName: "square.and.pencil")
) { ) {
DispatchQueue.main.async { DispatchQueue.main.async {
self.coordinator.present( self.sceneCoordinator?.present(
scene: .compose(viewModel: ComposeViewModel( scene: .compose(viewModel: ComposeViewModel(
context: self.context,
authenticationBox: self.authenticationBox, authenticationBox: self.authenticationBox,
composeContext: .composeStatus, composeContext: .composeStatus,
destination: .topLevel, destination: .topLevel,
@ -534,12 +533,12 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
assertionFailure("only works for status data provider") assertionFailure("only works for status data provider")
return return
} }
let userListViewModel = UserListViewModel( let userListViewModel = await UserListViewModel(
context: context, context: AppContext.shared,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
kind: .rebloggedBy(status: status) kind: .rebloggedBy(status: status)
) )
_ = await coordinator.present( _ = await self.sceneCoordinator?.present(
scene: .rebloggedBy(viewModel: userListViewModel), scene: .rebloggedBy(viewModel: userListViewModel),
from: self, from: self,
transition: .show transition: .show
@ -558,12 +557,12 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
assertionFailure("only works for status data provider") assertionFailure("only works for status data provider")
return return
} }
let userListViewModel = UserListViewModel( let userListViewModel = await UserListViewModel(
context: context, context: AppContext.shared,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
kind: .favoritedBy(status: status) kind: .favoritedBy(status: status)
) )
_ = await coordinator.present( _ = await self.sceneCoordinator?.present(
scene: .favoritedBy(viewModel: userListViewModel), scene: .favoritedBy(viewModel: userListViewModel),
from: self, from: self,
transition: .show transition: .show
@ -574,7 +573,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, statusMetricView: StatusMetricView, showEditHistory button: UIButton) { func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, statusMetricView: StatusMetricView, showEditHistory button: UIButton) {
Task { Task {
await coordinator.showLoading() await self.sceneCoordinator?.showLoading()
let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil) let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil)
guard let item = await self.item(from: source), guard let item = await self.item(from: source),
@ -586,12 +585,12 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
do { do {
let edits = try await APIService.shared.getHistory(forStatusID: status.id, authenticationBox: authenticationBox).value let edits = try await APIService.shared.getHistory(forStatusID: status.id, authenticationBox: authenticationBox).value
await coordinator.hideLoading() await self.sceneCoordinator?.hideLoading()
let viewModel = StatusEditHistoryViewModel(status: status, edits: edits, appContext: context, authenticationBox: authenticationBox) let viewModel = await StatusEditHistoryViewModel(status: status, edits: edits, appContext: AppContext.shared, authenticationBox: authenticationBox)
_ = await coordinator.present(scene: .editHistory(viewModel: viewModel), from: self, transition: .show) _ = await self.sceneCoordinator?.present(scene: .editHistory(viewModel: viewModel), from: self, transition: .show)
} catch { } catch {
await coordinator.hideLoading() await self.sceneCoordinator?.hideLoading()
} }
} }
} }

View File

@ -88,12 +88,11 @@ extension StatusTableViewControllerNavigateableCore where Self: DataSourceProvid
FeedbackGenerator.shared.generate(.selectionChanged) FeedbackGenerator.shared.generate(.selectionChanged)
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: self.context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
composeContext: .composeStatus, composeContext: .composeStatus,
destination: .reply(parent: status) destination: .reply(parent: status)
) )
_ = self.coordinator.present( _ = self.sceneCoordinator?.present(
scene: .compose(viewModel: composeViewModel), scene: .compose(viewModel: composeViewModel),
from: self, from: self,
transition: .modal(animated: true, completion: nil) transition: .modal(animated: true, completion: nil)

View File

@ -45,12 +45,11 @@ extension UITableViewDelegate where Self: DataSourceProvider & AuthContextProvid
) )
} else if let accountWarning = notification.entity.accountWarning { } else if let accountWarning = notification.entity.accountWarning {
let url = Mastodon.API.disputesEndpoint(domain: authenticationBox.domain, strikeId: accountWarning.id) let url = Mastodon.API.disputesEndpoint(domain: authenticationBox.domain, strikeId: accountWarning.id)
_ = coordinator.present( self.sceneCoordinator?.present(
scene: .safari(url: url), scene: .safari(url: url),
from: self, from: self,
transition: .safariPresent(animated: true, completion: nil) transition: .safariPresent(animated: true, completion: nil)
) )
} else { } else {
await DataSourceFacade.coordinateToProfileScene( await DataSourceFacade.coordinateToProfileScene(
provider: self, provider: self,
@ -135,7 +134,7 @@ extension UITableViewDelegate where Self: DataSourceProvider & MediaPreviewableV
title: L10n.Common.Alerts.SavePhotoFailure.title, title: L10n.Common.Alerts.SavePhotoFailure.title,
message: L10n.Common.Alerts.SavePhotoFailure.message message: L10n.Common.Alerts.SavePhotoFailure.message
) )
_ = self.coordinator.present( self.sceneCoordinator?.present(
scene: .alertController(alertController: alertController), scene: .alertController(alertController: alertController),
from: self, from: self,
transition: .alertController(animated: true, completion: nil) transition: .alertController(animated: true, completion: nil)
@ -166,10 +165,10 @@ extension UITableViewDelegate where Self: DataSourceProvider & MediaPreviewableV
attributes: [], attributes: [],
state: .off state: .off
) { [weak self] _ in ) { [weak self] _ in
guard let self = self else { return } guard let self = self, let coordinator = self.sceneCoordinator else { return }
Task { Task {
let applicationActivities: [UIActivity] = [ let applicationActivities: [UIActivity] = [
SafariActivity(sceneCoordinator: self.coordinator) SafariActivity(sceneCoordinator: coordinator)
] ]
let activityViewController = UIActivityViewController( let activityViewController = UIActivityViewController(
activityItems: [assetURL], activityItems: [assetURL],

View File

@ -36,7 +36,7 @@ extension DataSourceItem {
} }
} }
protocol DataSourceProvider: ViewControllerWithDependencies { protocol DataSourceProvider: UIViewController {
func item(from source: DataSourceItem.Source) async -> DataSourceItem? func item(from source: DataSourceItem.Source) async -> DataSourceItem?
func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent)

View File

@ -20,7 +20,6 @@ final class AccountListViewModel: NSObject {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
// output // output
@ -28,8 +27,7 @@ final class AccountListViewModel: NSObject {
var diffableDataSource: UITableViewDiffableDataSource<Section, Item>! var diffableDataSource: UITableViewDiffableDataSource<Section, Item>!
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
super.init() super.init()

View File

@ -12,10 +12,7 @@ import MastodonAsset
import MastodonLocalization import MastodonLocalization
import MastodonCore import MastodonCore
final class AccountListViewController: UIViewController, NeedsDependency { final class AccountListViewController: UIViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: AccountListViewModel! var viewModel: AccountListViewModel!
@ -83,7 +80,7 @@ extension AccountListViewController {
extension AccountListViewController { extension AccountListViewController {
@objc private func addBarButtonItem(_ sender: UIBarButtonItem) { @objc private func addBarButtonItem(_ sender: UIBarButtonItem) {
_ = coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil))
} }
override func accessibilityPerformEscape() -> Bool { override func accessibilityPerformEscape() -> Bool {
@ -115,7 +112,7 @@ extension AccountListViewController: UITableViewDelegate {
FileManager.default.invalidateHomeTimelineCache(for: userIdentifier) FileManager.default.invalidateHomeTimelineCache(for: userIdentifier)
FileManager.default.invalidateNotificationsAll(for: userIdentifier) FileManager.default.invalidateNotificationsAll(for: userIdentifier)
FileManager.default.invalidateNotificationsMentions(for: userIdentifier) FileManager.default.invalidateNotificationsMentions(for: userIdentifier)
self.coordinator.setup() self.sceneCoordinator?.setup()
} catch { } catch {
assertionFailure("Failed to delete Authentication: \(error)") assertionFailure("Failed to delete Authentication: \(error)")
@ -146,23 +143,23 @@ extension AccountListViewController: UITableViewDelegate {
Task { @MainActor in Task { @MainActor in
let isActive = AuthenticationServiceProvider.shared.activateExistingUser(record.userID, inDomain: record.domain) let isActive = AuthenticationServiceProvider.shared.activateExistingUser(record.userID, inDomain: record.domain)
guard isActive else { return } guard isActive else { return }
self.coordinator.setup() self.sceneCoordinator?.setup()
} // end Task } // end Task
case .addAccount: case .addAccount:
// TODO: add dismiss entry for welcome scene // TODO: add dismiss entry for welcome scene
_ = coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil))
case .logoutOfAllAccounts: case .logoutOfAllAccounts:
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let logoutAction = UIAlertAction(title: L10n.Scene.AccountList.logoutAllAccounts, style: .destructive) { _ in let logoutAction = UIAlertAction(title: L10n.Scene.AccountList.logoutAllAccounts, style: .destructive) { _ in
Task { @MainActor in Task { @MainActor in
self.coordinator.showLoading() self.sceneCoordinator?.showLoading()
for authenticationBox in AuthenticationServiceProvider.shared.mastodonAuthenticationBoxes { for authenticationBox in AuthenticationServiceProvider.shared.mastodonAuthenticationBoxes {
try? await AuthenticationServiceProvider.shared.signOutMastodonUser(authentication: authenticationBox.authentication) try? await AuthenticationServiceProvider.shared.signOutMastodonUser(authentication: authenticationBox.authentication)
} }
self.coordinator.hideLoading() self.sceneCoordinator?.hideLoading()
self.coordinator.setup() self.sceneCoordinator?.setup()
} }
} }

View File

@ -17,11 +17,8 @@ import MastodonUI
import MastodonLocalization import MastodonLocalization
import MastodonSDK import MastodonSDK
final class ComposeViewController: UIViewController, NeedsDependency { final class ComposeViewController: UIViewController {
static let minAutoCompleteVisibleHeight: CGFloat = 100 static let minAutoCompleteVisibleHeight: CGFloat = 100
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: ComposeViewModel var viewModel: ComposeViewModel
@ -48,7 +45,6 @@ final class ComposeViewController: UIViewController, NeedsDependency {
} }
return ComposeContentViewModel( return ComposeContentViewModel(
context: context,
authenticationBox: viewModel.authenticationBox, authenticationBox: viewModel.authenticationBox,
composeContext: composeContext, composeContext: composeContext,
destination: viewModel.destination, destination: viewModel.destination,
@ -217,7 +213,7 @@ extension ComposeViewController {
let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert) let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil) let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction) alertController.addAction(okAction)
_ = coordinator.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil))
return return
} }
@ -235,7 +231,7 @@ extension ComposeViewController {
self?.enqueuePublishStatus() self?.enqueuePublishStatus()
} }
alertController.addAction(confirmAction) alertController.addAction(confirmAction)
_ = coordinator.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil))
return return
} }
@ -266,7 +262,7 @@ extension ComposeViewController {
let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert) let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil) let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction) alertController.addAction(okAction)
_ = coordinator.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil))
return return
} }
@ -284,7 +280,7 @@ extension ComposeViewController {
self?.enqueuePublishStatusEdit() self?.enqueuePublishStatusEdit()
} }
alertController.addAction(confirmAction) alertController.addAction(confirmAction)
_ = coordinator.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil))
return return
} }

View File

@ -29,7 +29,6 @@ final class ComposeViewModel {
let id = UUID() let id = UUID()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let composeContext: Context let composeContext: Context
let destination: ComposeContentViewModel.Destination let destination: ComposeContentViewModel.Destination
@ -43,13 +42,11 @@ final class ComposeViewModel {
@Published var title: String @Published var title: String
init( init(
context: AppContext,
authenticationBox: MastodonAuthenticationBox, authenticationBox: MastodonAuthenticationBox,
composeContext: ComposeViewModel.Context, composeContext: ComposeViewModel.Context,
destination: ComposeContentViewModel.Destination, destination: ComposeContentViewModel.Destination,
initialContent: String = "" initialContent: String = ""
) { ) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.destination = destination self.destination = destination
self.initialContent = initialContent self.initialContent = initialContent

View File

@ -37,7 +37,6 @@ extension DiscoverySection {
static func diffableDataSource( static func diffableDataSource(
tableView: UITableView, tableView: UITableView,
context: AppContext,
configuration: Configuration configuration: Configuration
) -> UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem> { ) -> UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem> {

View File

@ -12,15 +12,12 @@ import MastodonAsset
import MastodonCore import MastodonCore
import MastodonUI import MastodonUI
public class DiscoveryViewController: PageboyViewController, NeedsDependency { public class DiscoveryViewController: PageboyViewController {
public static let containerViewMarginForRegularHorizontalSizeClass: CGFloat = 64 public static let containerViewMarginForRegularHorizontalSizeClass: CGFloat = 64
public static let containerViewMarginForCompactHorizontalSizeClass: CGFloat = 16 public static let containerViewMarginForCompactHorizontalSizeClass: CGFloat = 16
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var viewModel: DiscoveryViewModel! var viewModel: DiscoveryViewModel!

View File

@ -16,7 +16,6 @@ final class DiscoveryViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let discoveryPostsViewController: DiscoveryPostsViewController let discoveryPostsViewController: DiscoveryPostsViewController
let discoveryHashtagsViewController: DiscoveryHashtagsViewController let discoveryHashtagsViewController: DiscoveryHashtagsViewController
@ -26,37 +25,27 @@ final class DiscoveryViewModel {
@Published var viewControllers: [ScrollViewContainer] @Published var viewControllers: [ScrollViewContainer]
@MainActor @MainActor
init(context: AppContext, coordinator: SceneCoordinator, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
func setupDependency(_ needsDependency: NeedsDependency) {
needsDependency.context = context
needsDependency.coordinator = coordinator
}
discoveryPostsViewController = { discoveryPostsViewController = {
let viewController = DiscoveryPostsViewController() let viewController = DiscoveryPostsViewController()
setupDependency(viewController) viewController.viewModel = DiscoveryPostsViewModel(authenticationBox: authenticationBox)
viewController.viewModel = DiscoveryPostsViewModel(context: context, authenticationBox: authenticationBox)
return viewController return viewController
}() }()
discoveryHashtagsViewController = { discoveryHashtagsViewController = {
let viewController = DiscoveryHashtagsViewController() let viewController = DiscoveryHashtagsViewController()
setupDependency(viewController) viewController.viewModel = DiscoveryHashtagsViewModel(authenticationBox: authenticationBox)
viewController.viewModel = DiscoveryHashtagsViewModel(context: context, authenticationBox: authenticationBox)
return viewController return viewController
}() }()
discoveryNewsViewController = { discoveryNewsViewController = {
let viewController = DiscoveryNewsViewController() let viewController = DiscoveryNewsViewController()
setupDependency(viewController) viewController.viewModel = DiscoveryNewsViewModel(authenticationBox: authenticationBox)
viewController.viewModel = DiscoveryNewsViewModel(context: context, authenticationBox: authenticationBox)
return viewController return viewController
}() }()
discoveryForYouViewController = { discoveryForYouViewController = {
let viewController = DiscoveryForYouViewController() let viewController = DiscoveryForYouViewController()
setupDependency(viewController) viewController.viewModel = DiscoveryForYouViewModel(authenticationBox: authenticationBox)
viewController.viewModel = DiscoveryForYouViewModel(context: context, authenticationBox: authenticationBox)
return viewController return viewController
}() }()
self.viewControllers = [ self.viewControllers = [

View File

@ -11,10 +11,7 @@ import MastodonUI
import MastodonCore import MastodonCore
import MastodonSDK import MastodonSDK
final class DiscoveryForYouViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class DiscoveryForYouViewController: UIViewController, MediaPreviewableViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: DiscoveryForYouViewModel! var viewModel: DiscoveryForYouViewModel!
@ -137,7 +134,7 @@ extension DiscoveryForYouViewController: ProfileCardTableViewCellDelegate {
guard let indexPath = tableView.indexPath(for: cell) else { return } guard let indexPath = tableView.indexPath(for: cell) else { return }
guard case let .account(account, _) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return } guard case let .account(account, _) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
coordinator.showLoading() self.sceneCoordinator?.showLoading()
Task { [weak self] in Task { [weak self] in
@ -147,16 +144,15 @@ extension DiscoveryForYouViewController: ProfileCardTableViewCellDelegate {
let familiarFollowers = viewModel.familiarFollowers.first(where: { $0.id == userID })?.accounts ?? [] let familiarFollowers = viewModel.familiarFollowers.first(where: { $0.id == userID })?.accounts ?? []
let relationships = try await APIService.shared.relationship(forAccounts: familiarFollowers, authenticationBox: authenticationBox).value let relationships = try await APIService.shared.relationship(forAccounts: familiarFollowers, authenticationBox: authenticationBox).value
coordinator.hideLoading() self.sceneCoordinator?.hideLoading()
let familiarFollowersViewModel = FamiliarFollowersViewModel( let familiarFollowersViewModel = FamiliarFollowersViewModel(
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
accounts: familiarFollowers, accounts: familiarFollowers,
relationships: relationships relationships: relationships
) )
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .familiarFollowers(viewModel: familiarFollowersViewModel), scene: .familiarFollowers(viewModel: familiarFollowersViewModel),
from: self, from: self,
transition: .show transition: .show

View File

@ -17,7 +17,6 @@ extension DiscoveryForYouViewModel {
) { ) {
diffableDataSource = DiscoverySection.diffableDataSource( diffableDataSource = DiscoverySection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: DiscoverySection.Configuration( configuration: DiscoverySection.Configuration(
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
profileCardTableViewCellDelegate: profileCardTableViewCellDelegate, profileCardTableViewCellDelegate: profileCardTableViewCellDelegate,

View File

@ -16,7 +16,6 @@ final class DiscoveryForYouViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
@MainActor @MainActor
@ -29,8 +28,7 @@ final class DiscoveryForYouViewModel {
var diffableDataSource: UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem>? var diffableDataSource: UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem>?
let didLoadLatest = PassthroughSubject<Void, Never>() let didLoadLatest = PassthroughSubject<Void, Never>()
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.accounts = [] self.accounts = []
self.relationships = [] self.relationships = []

View File

@ -10,10 +10,7 @@ import Combine
import MastodonCore import MastodonCore
import MastodonUI import MastodonUI
final class DiscoveryHashtagsViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class DiscoveryHashtagsViewController: UIViewController, MediaPreviewableViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: DiscoveryHashtagsViewModel! var viewModel: DiscoveryHashtagsViewModel!
@ -88,8 +85,8 @@ extension DiscoveryHashtagsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard case let .hashtag(tag) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return } guard case let .hashtag(tag) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
let hashtagTimelineViewModel = HashtagTimelineViewModel(context: context, authenticationBox: viewModel.authenticationBox, hashtag: tag.name) let hashtagTimelineViewModel = HashtagTimelineViewModel(authenticationBox: viewModel.authenticationBox, hashtag: tag.name)
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
from: self, from: self,
transition: .show transition: .show
@ -198,8 +195,8 @@ extension DiscoveryHashtagsViewController: TableViewControllerNavigateable {
guard let item = diffableDataSource.itemIdentifier(for: indexPathForSelectedRow) else { return } guard let item = diffableDataSource.itemIdentifier(for: indexPathForSelectedRow) else { return }
guard case let .hashtag(tag) = item else { return } guard case let .hashtag(tag) = item else { return }
let hashtagTimelineViewModel = HashtagTimelineViewModel(context: context, authenticationBox: viewModel.authenticationBox, hashtag: tag.name) let hashtagTimelineViewModel = HashtagTimelineViewModel(authenticationBox: viewModel.authenticationBox, hashtag: tag.name)
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
from: self, from: self,
transition: .show transition: .show

View File

@ -14,7 +14,6 @@ extension DiscoveryHashtagsViewModel {
) { ) {
diffableDataSource = DiscoverySection.diffableDataSource( diffableDataSource = DiscoverySection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: DiscoverySection.Configuration(authenticationBox: authenticationBox) configuration: DiscoverySection.Configuration(authenticationBox: authenticationBox)
) )

View File

@ -18,7 +18,6 @@ final class DiscoveryHashtagsViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let viewDidAppeared = PassthroughSubject<Void, Never>() let viewDidAppeared = PassthroughSubject<Void, Never>()
@ -26,8 +25,7 @@ final class DiscoveryHashtagsViewModel {
var diffableDataSource: UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem>? var diffableDataSource: UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem>?
@Published var hashtags: [Mastodon.Entity.Tag] = [] @Published var hashtags: [Mastodon.Entity.Tag] = []
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
// end init // end init

View File

@ -10,10 +10,7 @@ import Combine
import MastodonCore import MastodonCore
import MastodonUI import MastodonUI
final class DiscoveryNewsViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class DiscoveryNewsViewController: UIViewController, MediaPreviewableViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: DiscoveryNewsViewModel! var viewModel: DiscoveryNewsViewModel!
@ -85,7 +82,7 @@ extension DiscoveryNewsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard case let .link(link) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return } guard case let .link(link) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
guard let url = URL(string: link.url) else { return } guard let url = URL(string: link.url) else { return }
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .safari(url: url), scene: .safari(url: url),
from: self, from: self,
transition: .safariPresent(animated: true, completion: nil) transition: .safariPresent(animated: true, completion: nil)
@ -182,7 +179,7 @@ extension DiscoveryNewsViewController: TableViewControllerNavigateable {
guard case let .link(link) = item else { return } guard case let .link(link) = item else { return }
guard let url = URL(string: link.url) else { return } guard let url = URL(string: link.url) else { return }
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .safari(url: url), scene: .safari(url: url),
from: self, from: self,
transition: .safariPresent(animated: true, completion: nil) transition: .safariPresent(animated: true, completion: nil)

View File

@ -15,7 +15,6 @@ extension DiscoveryNewsViewModel {
) { ) {
diffableDataSource = DiscoverySection.diffableDataSource( diffableDataSource = DiscoverySection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: DiscoverySection.Configuration(authenticationBox: authenticationBox) configuration: DiscoverySection.Configuration(authenticationBox: authenticationBox)
) )

View File

@ -18,7 +18,6 @@ final class DiscoveryNewsViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
// output // output
@ -40,8 +39,7 @@ final class DiscoveryNewsViewModel {
let didLoadLatest = PassthroughSubject<Void, Never>() let didLoadLatest = PassthroughSubject<Void, Never>()
@Published var isServerSupportEndpoint = true @Published var isServerSupportEndpoint = true
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
// end init // end init

View File

@ -10,9 +10,7 @@ import Combine
import MastodonCore import MastodonCore
import MastodonUI import MastodonUI
final class DiscoveryPostsViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class DiscoveryPostsViewController: UIViewController, MediaPreviewableViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: DiscoveryPostsViewModel! var viewModel: DiscoveryPostsViewModel!

View File

@ -16,9 +16,7 @@ extension DiscoveryPostsViewModel {
) { ) {
diffableDataSource = StatusSection.diffableDataSource( diffableDataSource = StatusSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: StatusSection.Configuration( configuration: StatusSection.Configuration(
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
statusTableViewCellDelegate: statusTableViewCellDelegate, statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil, timelineMiddleLoaderTableViewCellDelegate: nil,

View File

@ -18,7 +18,6 @@ final class DiscoveryPostsViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let dataController: StatusDataController let dataController: StatusDataController
@ -41,8 +40,7 @@ final class DiscoveryPostsViewModel {
@Published var isServerSupportEndpoint = true @Published var isServerSupportEndpoint = true
@MainActor @MainActor
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.dataController = StatusDataController() self.dataController = StatusDataController()

View File

@ -10,17 +10,15 @@ import UIKit
class NewDonationNavigationFlow: NavigationFlow { class NewDonationNavigationFlow: NavigationFlow {
private let campaign: DonationCampaignViewModel private let campaign: DonationCampaignViewModel
private let appContext: AppContext
private let authenticationBox: MastodonAuthenticationBox private let authenticationBox: MastodonAuthenticationBox
private let sceneCoordinator: SceneCoordinator private let sceneCoordinator: SceneCoordinator
init( init(
flowPresenter: NavigationFlowPresenter, flowPresenter: NavigationFlowPresenter,
campaign: DonationCampaignViewModel, appContext: AppContext, campaign: DonationCampaignViewModel,
authenticationBox: MastodonAuthenticationBox, sceneCoordinator: SceneCoordinator authenticationBox: MastodonAuthenticationBox, sceneCoordinator: SceneCoordinator
) { ) {
self.campaign = campaign self.campaign = campaign
self.appContext = appContext
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.sceneCoordinator = sceneCoordinator self.sceneCoordinator = sceneCoordinator
super.init(flowPresenter: flowPresenter) super.init(flowPresenter: flowPresenter)
@ -106,7 +104,6 @@ class NewDonationNavigationFlow: NavigationFlow {
private func composeDonationSuccessPost(_ suggestedText: String) { private func composeDonationSuccessPost(_ suggestedText: String) {
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: appContext,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
composeContext: .composeStatus, composeContext: .composeStatus,
destination: .topLevel, destination: .topLevel,

View File

@ -16,10 +16,7 @@ import MastodonUI
import MastodonLocalization import MastodonLocalization
import MastodonSDK import MastodonSDK
final class HashtagTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class HashtagTimelineViewController: UIViewController, MediaPreviewableViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
let mediaPreviewTransitionController = MediaPreviewTransitionController() let mediaPreviewTransitionController = MediaPreviewTransitionController()
@ -184,13 +181,12 @@ extension HashtagTimelineViewController {
let hashtag = "#" + viewModel.hashtag let hashtag = "#" + viewModel.hashtag
UITextChecker.learnWord(hashtag) UITextChecker.learnWord(hashtag)
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: context,
authenticationBox: viewModel.authenticationBox, authenticationBox: viewModel.authenticationBox,
composeContext: .composeStatus, composeContext: .composeStatus,
destination: .topLevel, destination: .topLevel,
initialContent: hashtag initialContent: hashtag
) )
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
} }
} }

View File

@ -17,9 +17,7 @@ extension HashtagTimelineViewModel {
) { ) {
diffableDataSource = StatusSection.diffableDataSource( diffableDataSource = StatusSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: StatusSection.Configuration( configuration: StatusSection.Configuration(
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
statusTableViewCellDelegate: statusTableViewCellDelegate, statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil, timelineMiddleLoaderTableViewCellDelegate: nil,

View File

@ -22,7 +22,6 @@ final class HashtagTimelineViewModel {
var needLoadMiddleIndex: Int? = nil var needLoadMiddleIndex: Int? = nil
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let dataController: StatusDataController let dataController: StatusDataController
let isFetchingLatestTimeline = CurrentValueSubject<Bool, Never>(false) let isFetchingLatestTimeline = CurrentValueSubject<Bool, Never>(false)
@ -50,8 +49,7 @@ final class HashtagTimelineViewModel {
}() }()
@MainActor @MainActor
init(context: AppContext, authenticationBox: MastodonAuthenticationBox, hashtag: String) { init(authenticationBox: MastodonAuthenticationBox, hashtag: String) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.hashtag = hashtag self.hashtag = hashtag
self.dataController = StatusDataController() self.dataController = StatusDataController()

View File

@ -19,10 +19,7 @@ import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization import MastodonLocalization
final class HomeTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class HomeTimelineViewController: UIViewController, MediaPreviewableViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: HomeTimelineViewModel? var viewModel: HomeTimelineViewModel?
@ -616,9 +613,9 @@ extension HomeTimelineViewController {
@objc private func findPeopleButtonPressed(_ sender: Any?) { @objc private func findPeopleButtonPressed(_ sender: Any?) {
guard let authenticationBox = viewModel?.authenticationBox else { return } guard let authenticationBox = viewModel?.authenticationBox else { return }
let suggestionAccountViewModel = SuggestionAccountViewModel(context: context, authenticationBox: authenticationBox) let suggestionAccountViewModel = SuggestionAccountViewModel(authenticationBox: authenticationBox)
suggestionAccountViewModel.delegate = viewModel suggestionAccountViewModel.delegate = viewModel
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .suggestionAccount(viewModel: suggestionAccountViewModel), scene: .suggestionAccount(viewModel: suggestionAccountViewModel),
from: self, from: self,
transition: .modal(animated: true, completion: nil) transition: .modal(animated: true, completion: nil)
@ -626,13 +623,13 @@ extension HomeTimelineViewController {
} }
@objc private func manuallySearchButtonPressed(_ sender: UIButton) { @objc private func manuallySearchButtonPressed(_ sender: UIButton) {
coordinator.switchToTabBar(tab: .search) self.sceneCoordinator?.switchToTabBar(tab: .search)
} }
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) { @objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
guard let setting = SettingService.shared.currentSetting.value else { return } guard let setting = SettingService.shared.currentSetting.value else { return }
_ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none) _ = self.sceneCoordinator?.present(scene: .settings(setting: setting), from: self, transition: .none)
} }
@objc private func refreshControlValueChanged(_ sender: RefreshControl) { @objc private func refreshControlValueChanged(_ sender: RefreshControl) {
@ -650,7 +647,7 @@ extension HomeTimelineViewController {
FileManager.default.invalidateHomeTimelineCache(for: userIdentifier) FileManager.default.invalidateHomeTimelineCache(for: userIdentifier)
FileManager.default.invalidateNotificationsAll(for: userIdentifier) FileManager.default.invalidateNotificationsAll(for: userIdentifier)
FileManager.default.invalidateNotificationsMentions(for: userIdentifier) FileManager.default.invalidateNotificationsMentions(for: userIdentifier)
self.coordinator.setup() self.sceneCoordinator?.setup()
} }
} }
@ -741,7 +738,8 @@ extension HomeTimelineViewController {
private func showDonationCampaign(_ campaign: Mastodon.Entity.DonationCampaign) { private func showDonationCampaign(_ campaign: Mastodon.Entity.DonationCampaign) {
hideDonationCampaignBanner() hideDonationCampaignBanner()
navigationFlow = NewDonationNavigationFlow(flowPresenter: self, campaign: campaign, appContext: context, authenticationBox: authenticationBox, sceneCoordinator: coordinator) guard let coordinator = self.sceneCoordinator else { return }
navigationFlow = NewDonationNavigationFlow(flowPresenter: self, campaign: campaign, authenticationBox: authenticationBox, sceneCoordinator: coordinator)
navigationFlow?.presentFlow { [weak self] in navigationFlow?.presentFlow { [weak self] in
self?.navigationFlow = nil self?.navigationFlow = nil
} }

View File

@ -20,9 +20,7 @@ extension HomeTimelineViewModel {
) { ) {
diffableDataSource = StatusSection.diffableDataSource( diffableDataSource = StatusSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: StatusSection.Configuration( configuration: StatusSection.Configuration(
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
statusTableViewCellDelegate: statusTableViewCellDelegate, statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: timelineMiddleLoaderTableViewCellDelegate, timelineMiddleLoaderTableViewCellDelegate: timelineMiddleLoaderTableViewCellDelegate,

View File

@ -23,7 +23,6 @@ final class HomeTimelineViewModel: NSObject {
var observations = Set<NSKeyValueObservation>() var observations = Set<NSKeyValueObservation>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let dataController: FeedDataController let dataController: FeedDataController
@ -91,10 +90,9 @@ final class HomeTimelineViewModel: NSObject {
var cellFrameCache = NSCache<NSNumber, NSValue>() var cellFrameCache = NSCache<NSNumber, NSValue>()
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.dataController = FeedDataController(context: context, authenticationBox: authenticationBox, kind: .home(timeline: timelineContext)) self.dataController = FeedDataController(authenticationBox: authenticationBox, kind: .home(timeline: timelineContext))
super.init() super.init()
self.dataController.records = (try? PersistenceManager.shared.cachedTimeline(.homeTimeline(authenticationBox)).map { self.dataController.records = (try? PersistenceManager.shared.cachedTimeline(.homeTimeline(authenticationBox)).map {
MastodonFeed.fromStatus($0, kind: .home) MastodonFeed.fromStatus($0, kind: .home)

View File

@ -13,10 +13,7 @@ import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization import MastodonLocalization
final class MediaPreviewViewController: UIViewController, NeedsDependency { final class MediaPreviewViewController: UIViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: MediaPreviewViewModel! var viewModel: MediaPreviewViewModel!
@ -285,7 +282,7 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
title: L10n.Common.Alerts.SavePhotoFailure.title, title: L10n.Common.Alerts.SavePhotoFailure.title,
message: L10n.Common.Alerts.SavePhotoFailure.message message: L10n.Common.Alerts.SavePhotoFailure.message
) )
_ = self.coordinator.present( _ = self.sceneCoordinator?.present(
scene: .alertController(alertController: alertController), scene: .alertController(alertController: alertController),
from: self, from: self,
transition: .alertController(animated: true, completion: nil) transition: .alertController(animated: true, completion: nil)
@ -296,7 +293,7 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
} receiveValue: { _ in } receiveValue: { _ in
// do nothing // do nothing
} }
.store(in: &context.disposeBag) .store(in: &AppContext.shared.disposeBag)
case .copyPhoto: case .copyPhoto:
guard let assetURL = viewController.viewModel.item.assetURL else { return } guard let assetURL = viewController.viewModel.item.assetURL else { return }
@ -311,10 +308,10 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
} receiveValue: { _ in } receiveValue: { _ in
// do nothing // do nothing
} }
.store(in: &context.disposeBag) .store(in: &AppContext.shared.disposeBag)
case .share: case .share:
let applicationActivities: [UIActivity] = [ let applicationActivities: [UIActivity] = [
SafariActivity(sceneCoordinator: self.coordinator) SafariActivity(sceneCoordinator: self.sceneCoordinator)
] ]
let activityViewController = UIActivityViewController( let activityViewController = UIActivityViewController(
activityItems: { activityItems: {

View File

@ -15,10 +15,10 @@ class AccountNotificationTimelineViewController: NotificationTimelineViewControl
let request: Mastodon.Entity.NotificationRequest let request: Mastodon.Entity.NotificationRequest
weak var delegate: AccountNotificationTimelineViewControllerDelegate? weak var delegate: AccountNotificationTimelineViewControllerDelegate?
init(viewModel: NotificationTimelineViewModel, context: AppContext, coordinator: SceneCoordinator, notificationRequest: Mastodon.Entity.NotificationRequest) { init(viewModel: NotificationTimelineViewModel, notificationRequest: Mastodon.Entity.NotificationRequest) {
self.request = notificationRequest self.request = notificationRequest
super.init(viewModel: viewModel, context: context, coordinator: coordinator) super.init(viewModel: viewModel)
navigationItem.rightBarButtonItem = UIBarButtonItem(title: nil, image: UIImage(systemName: "ellipsis.circle"), target: nil, action: nil, menu: menu()) navigationItem.rightBarButtonItem = UIBarButtonItem(title: nil, image: UIImage(systemName: "ellipsis.circle"), target: nil, action: nil, menu: menu())
} }
@ -32,18 +32,18 @@ class AccountNotificationTimelineViewController: NotificationTimelineViewControl
UIAction(title: L10n.Scene.Notification.FilteredNotification.accept, image: UIImage(systemName: "checkmark")) { [weak self] _ in UIAction(title: L10n.Scene.Notification.FilteredNotification.accept, image: UIImage(systemName: "checkmark")) { [weak self] _ in
guard let self else { return } guard let self else { return }
coordinator.showLoading() self.sceneCoordinator?.showLoading()
self.navigationController?.popViewController(animated: true) self.navigationController?.popViewController(animated: true)
self.delegate?.acceptRequest(self, request: request) self.delegate?.acceptRequest(self, request: request)
coordinator.hideLoading() self.sceneCoordinator?.hideLoading()
}, },
UIAction(title: L10n.Scene.Notification.FilteredNotification.dismiss, image: NotificationRequestConstants.dismissIcon) { [weak self] _ in UIAction(title: L10n.Scene.Notification.FilteredNotification.dismiss, image: NotificationRequestConstants.dismissIcon) { [weak self] _ in
guard let self else { return } guard let self else { return }
coordinator.showLoading() self.sceneCoordinator?.showLoading()
self.navigationController?.popViewController(animated: true) self.navigationController?.popViewController(animated: true)
self.delegate?.dismissRequest(self, request: request) self.delegate?.dismissRequest(self, request: request)
coordinator.hideLoading() self.sceneCoordinator?.hideLoading()
} }
]) ])

View File

@ -18,9 +18,8 @@ protocol NotificationRequestsTableViewControllerDelegate: AnyObject {
func notificationRequestsUpdated(_ viewController: NotificationRequestsTableViewController) func notificationRequestsUpdated(_ viewController: NotificationRequestsTableViewController)
} }
class NotificationRequestsTableViewController: UIViewController, NeedsDependency { class NotificationRequestsTableViewController: UIViewController {
var context: AppContext!
var coordinator: SceneCoordinator!
weak var delegate: NotificationRequestsTableViewControllerDelegate? weak var delegate: NotificationRequestsTableViewControllerDelegate?
let tableView: UITableView let tableView: UITableView
@ -30,8 +29,6 @@ class NotificationRequestsTableViewController: UIViewController, NeedsDependency
init(viewModel: NotificationRequestsViewModel) { init(viewModel: NotificationRequestsViewModel) {
self.viewModel = viewModel self.viewModel = viewModel
self.context = viewModel.appContext
self.coordinator = viewModel.coordinator
tableView = UITableView(frame: .zero) tableView = UITableView(frame: .zero)
tableView.translatesAutoresizingMaskIntoConstraints = false tableView.translatesAutoresizingMaskIntoConstraints = false

View File

@ -5,16 +5,12 @@ import MastodonSDK
import MastodonCore import MastodonCore
struct NotificationRequestsViewModel { struct NotificationRequestsViewModel {
let appContext: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let coordinator: SceneCoordinator
var requests: [Mastodon.Entity.NotificationRequest] var requests: [Mastodon.Entity.NotificationRequest]
init(appContext: AppContext, authenticationBox: MastodonAuthenticationBox, coordinator: SceneCoordinator, requests: [Mastodon.Entity.NotificationRequest]) { init(authenticationBox: MastodonAuthenticationBox, requests: [Mastodon.Entity.NotificationRequest]) {
self.appContext = appContext
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.coordinator = coordinator
self.requests = requests self.requests = requests
} }
} }

View File

@ -32,7 +32,6 @@ extension NotificationSection {
static func diffableDataSource( static func diffableDataSource(
tableView: UITableView, tableView: UITableView,
context: AppContext,
configuration: Configuration configuration: Configuration
) -> UITableViewDiffableDataSource<NotificationSection, NotificationItem> { ) -> UITableViewDiffableDataSource<NotificationSection, NotificationItem> {
tableView.register(NotificationTableViewCell.self, forCellReuseIdentifier: String(describing: NotificationTableViewCell.self)) tableView.register(NotificationTableViewCell.self, forCellReuseIdentifier: String(describing: NotificationTableViewCell.self))
@ -50,7 +49,6 @@ extension NotificationSection {
} else { } else {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationTableViewCell.self), for: indexPath) as! NotificationTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationTableViewCell.self), for: indexPath) as! NotificationTableViewCell
configure( configure(
context: context,
tableView: tableView, tableView: tableView,
cell: cell, cell: cell,
viewModel: NotificationTableViewCell.ViewModel(value: .feed(feed)), viewModel: NotificationTableViewCell.ViewModel(value: .feed(feed)),
@ -81,20 +79,17 @@ extension NotificationSection {
extension NotificationSection { extension NotificationSection {
static func configure( static func configure(
context: AppContext,
tableView: UITableView, tableView: UITableView,
cell: NotificationTableViewCell, cell: NotificationTableViewCell,
viewModel: NotificationTableViewCell.ViewModel, viewModel: NotificationTableViewCell.ViewModel,
configuration: Configuration configuration: Configuration
) { ) {
StatusSection.setupStatusPollDataSource( StatusSection.setupStatusPollDataSource(
context: context,
authenticationBox: configuration.authenticationBox, authenticationBox: configuration.authenticationBox,
statusView: cell.notificationView.statusView statusView: cell.notificationView.statusView
) )
StatusSection.setupStatusPollDataSource( StatusSection.setupStatusPollDataSource(
context: context,
authenticationBox: configuration.authenticationBox, authenticationBox: configuration.authenticationBox,
statusView: cell.notificationView.quoteStatusView statusView: cell.notificationView.quoteStatusView
) )

View File

@ -11,10 +11,7 @@ import CoreDataStack
import MastodonCore import MastodonCore
import MastodonLocalization import MastodonLocalization
class NotificationTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { class NotificationTimelineViewController: UIViewController, MediaPreviewableViewController {
weak var context: AppContext!
weak var coordinator: SceneCoordinator!
let mediaPreviewTransitionController = MediaPreviewTransitionController() let mediaPreviewTransitionController = MediaPreviewTransitionController()
@ -39,10 +36,8 @@ class NotificationTimelineViewController: UIViewController, NeedsDependency, Med
let cellFrameCache = NSCache<NSNumber, NSValue>() let cellFrameCache = NSCache<NSNumber, NSValue>()
init(viewModel: NotificationTimelineViewModel, context: AppContext, coordinator: SceneCoordinator) { init(viewModel: NotificationTimelineViewModel) {
self.viewModel = viewModel self.viewModel = viewModel
self.context = context
self.coordinator = coordinator
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
@ -288,11 +283,10 @@ extension NotificationTimelineViewController: TableViewControllerNavigateable {
if let status = notification.status { if let status = notification.status {
let threadViewModel = ThreadViewModel( let threadViewModel = ThreadViewModel(
context: self.context,
authenticationBox: self.viewModel.authenticationBox, authenticationBox: self.viewModel.authenticationBox,
optionalRoot: .root(context: .init(status: .fromEntity(status))) optionalRoot: .root(context: .init(status: .fromEntity(status)))
) )
_ = self.coordinator.present( _ = self.sceneCoordinator?.present(
scene: .thread(viewModel: threadViewModel), scene: .thread(viewModel: threadViewModel),
from: self, from: self,
transition: .show transition: .show

View File

@ -18,7 +18,6 @@ extension NotificationTimelineViewModel {
) { ) {
diffableDataSource = NotificationSection.diffableDataSource( diffableDataSource = NotificationSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: NotificationSection.Configuration( configuration: NotificationSection.Configuration(
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
notificationTableViewCellDelegate: notificationTableViewCellDelegate, notificationTableViewCellDelegate: notificationTableViewCellDelegate,

View File

@ -19,7 +19,6 @@ final class NotificationTimelineViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let scope: Scope let scope: Scope
var notificationPolicy: Mastodon.Entity.NotificationPolicy? var notificationPolicy: Mastodon.Entity.NotificationPolicy?
@ -47,15 +46,13 @@ final class NotificationTimelineViewModel {
@MainActor @MainActor
init( init(
context: AppContext,
authenticationBox: MastodonAuthenticationBox, authenticationBox: MastodonAuthenticationBox,
scope: Scope, scope: Scope,
notificationPolicy: Mastodon.Entity.NotificationPolicy? = nil notificationPolicy: Mastodon.Entity.NotificationPolicy? = nil
) { ) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.scope = scope self.scope = scope
self.dataController = FeedDataController(context: context, authenticationBox: authenticationBox, kind: scope.feedKind) self.dataController = FeedDataController(authenticationBox: authenticationBox, kind: scope.feedKind)
self.notificationPolicy = notificationPolicy self.notificationPolicy = notificationPolicy
switch scope { switch scope {

View File

@ -14,10 +14,7 @@ import Pageboy
import MastodonCore import MastodonCore
import MastodonSDK import MastodonSDK
final class NotificationViewController: TabmanViewController, NeedsDependency { final class NotificationViewController: TabmanViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var observations = Set<NSKeyValueObservation>() var observations = Set<NSKeyValueObservation>()
@ -129,7 +126,7 @@ extension NotificationViewController {
privateMentions: policy.filterPrivateMentions privateMentions: policy.filterPrivateMentions
) )
guard let policyViewController = coordinator.present(scene: .notificationPolicy(viewModel: policyViewModel), transition: .formSheet) as? NotificationPolicyViewController else { return } guard let policyViewController = self.sceneCoordinator?.present(scene: .notificationPolicy(viewModel: policyViewModel), transition: .formSheet) as? NotificationPolicyViewController else { return }
policyViewController.delegate = self policyViewController.delegate = self
} }
@ -156,12 +153,9 @@ extension NotificationViewController {
let viewController = NotificationTimelineViewController( let viewController = NotificationTimelineViewController(
viewModel: NotificationTimelineViewModel( viewModel: NotificationTimelineViewModel(
context: context,
authenticationBox: viewModel.authenticationBox, authenticationBox: viewModel.authenticationBox,
scope: scope, notificationPolicy: viewModel.notificationPolicy scope: scope, notificationPolicy: viewModel.notificationPolicy
), )
context: context,
coordinator: coordinator
) )
return viewController return viewController

View File

@ -13,13 +13,10 @@ import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization import MastodonLocalization
final class MastodonConfirmEmailViewController: UIViewController, NeedsDependency { final class MastodonConfirmEmailViewController: UIViewController {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var viewModel: MastodonConfirmEmailViewModel! var viewModel: MastodonConfirmEmailViewModel!
let stackView = UIStackView() let stackView = UIStackView()
@ -126,10 +123,10 @@ extension MastodonConfirmEmailViewController {
} receiveValue: { _ in } receiveValue: { _ in
// do nothing // do nothing
} }
.store(in: &self.context.disposeBag) // execute in the background .store(in: &AppContext.shared.disposeBag) // execute in the background
} // end switch } // end switch
} receiveValue: { _ in } receiveValue: { _ in
self.coordinator.setup() self.sceneCoordinator?.setup()
// self.dismiss(animated: true, completion: nil) // self.dismiss(animated: true, completion: nil)
} }
.store(in: &self.disposeBag) .store(in: &self.disposeBag)
@ -208,13 +205,13 @@ extension MastodonConfirmEmailViewController {
let resendAction = UIAlertAction(title: L10n.Scene.ConfirmEmail.DontReceiveEmail.resendEmail, style: .default) { _ in let resendAction = UIAlertAction(title: L10n.Scene.ConfirmEmail.DontReceiveEmail.resendEmail, style: .default) { _ in
let url = Mastodon.API.resendEmailURL(domain: self.viewModel.authenticateInfo.domain) let url = Mastodon.API.resendEmailURL(domain: self.viewModel.authenticateInfo.domain)
let viewModel = MastodonResendEmailViewModel(resendEmailURL: url, email: self.viewModel.email) let viewModel = MastodonResendEmailViewModel(resendEmailURL: url, email: self.viewModel.email)
_ = self.coordinator.present(scene: .mastodonResendEmail(viewModel: viewModel), from: self, transition: .modal(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .mastodonResendEmail(viewModel: viewModel), from: self, transition: .modal(animated: true, completion: nil))
} }
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) { _ in let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) { _ in
} }
alertController.addAction(resendAction) alertController.addAction(resendAction)
alertController.addAction(okAction) alertController.addAction(okAction)
_ = self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
} }
} }

View File

@ -17,7 +17,7 @@ enum CategoryPickerSection: Equatable, Hashable {
extension CategoryPickerSection { extension CategoryPickerSection {
static func collectionViewDiffableDataSource( static func collectionViewDiffableDataSource(
for collectionView: UICollectionView, for collectionView: UICollectionView,
dependency: NeedsDependency, dependency: UIViewController,
viewModel: MastodonPickServerViewModel viewModel: MastodonPickServerViewModel
) -> UICollectionViewDiffableDataSource<CategoryPickerSection, CategoryPickerItem> { ) -> UICollectionViewDiffableDataSource<CategoryPickerSection, CategoryPickerItem> {
UICollectionViewDiffableDataSource(collectionView: collectionView) { [weak dependency] collectionView, indexPath, item -> UICollectionViewCell? in UICollectionViewDiffableDataSource(collectionView: collectionView) { [weak dependency] collectionView, indexPath, item -> UICollectionViewCell? in

View File

@ -14,11 +14,7 @@ import MastodonLocalization
import MastodonUI import MastodonUI
import MastodonSDK import MastodonSDK
final class MastodonPickServerViewController: UIViewController, NeedsDependency { final class MastodonPickServerViewController: UIViewController {
var context: AppContext! {
get { return AppContext.shared }
set { }
}
var coordinator: SceneCoordinator! var coordinator: SceneCoordinator!

View File

@ -13,7 +13,7 @@ extension MastodonPickServerViewModel {
func setupDiffableDataSource( func setupDiffableDataSource(
for tableView: UITableView, for tableView: UITableView,
dependency: NeedsDependency, dependency: UIViewController,
pickServerServerSectionTableHeaderViewDelegate: PickServerServerSectionTableHeaderViewDelegate pickServerServerSectionTableHeaderViewDelegate: PickServerServerSectionTableHeaderViewDelegate
) { ) {
// set section header // set section header

View File

@ -18,7 +18,7 @@ enum PickServerSection: Equatable, Hashable {
extension PickServerSection { extension PickServerSection {
static func tableViewDiffableDataSource( static func tableViewDiffableDataSource(
for tableView: UITableView, for tableView: UITableView,
dependency: NeedsDependency dependency: UIViewController
) -> UITableViewDiffableDataSource<PickServerSection, PickServerItem> { ) -> UITableViewDiffableDataSource<PickServerSection, PickServerItem> {
tableView.register(PickServerCell.self, forCellReuseIdentifier: String(describing: PickServerCell.self)) tableView.register(PickServerCell.self, forCellReuseIdentifier: String(describing: PickServerCell.self))
tableView.register(PickServerLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: PickServerLoaderTableViewCell.self)) tableView.register(PickServerLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: PickServerLoaderTableViewCell.self))

View File

@ -16,15 +16,12 @@ import MastodonAsset
import MastodonCore import MastodonCore
import MastodonLocalization import MastodonLocalization
final class MastodonRegisterViewController: UIViewController, NeedsDependency, OnboardingViewControllerAppearance { final class MastodonRegisterViewController: UIViewController, OnboardingViewControllerAppearance {
static let avatarImageMaxSizeInPixel = CGSize(width: 400, height: 400) static let avatarImageMaxSizeInPixel = CGSize(width: 400, height: 400)
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
private var observations = Set<NSKeyValueObservation>() private var observations = Set<NSKeyValueObservation>()
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var viewModel: MastodonRegisterViewModel! var viewModel: MastodonRegisterViewModel!
private(set) lazy var mastodonRegisterView = MastodonRegisterView(viewModel: viewModel) private(set) lazy var mastodonRegisterView = MastodonRegisterView(viewModel: viewModel)
@ -92,7 +89,7 @@ extension MastodonRegisterViewController {
let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.SignUpFailure.title, preferredStyle: .alert) let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.SignUpFailure.title, preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil) let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction) alertController.addAction(okAction)
_ = self.coordinator.present( _ = self.sceneCoordinator?.present(
scene: .alertController(alertController: alertController), scene: .alertController(alertController: alertController),
from: nil, from: nil,
transition: .alertController(animated: true, completion: nil) transition: .alertController(animated: true, completion: nil)

View File

@ -10,10 +10,7 @@ import UIKit
import WebKit import WebKit
import MastodonCore import MastodonCore
final class MastodonResendEmailViewController: UIViewController, NeedsDependency { final class MastodonResendEmailViewController: UIViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: MastodonResendEmailViewModel! var viewModel: MastodonResendEmailViewModel!

View File

@ -12,7 +12,7 @@ import MastodonCore
import MastodonLocalization import MastodonLocalization
import MastodonSDK import MastodonSDK
final class WelcomeViewController: UIViewController, NeedsDependency { final class WelcomeViewController: UIViewController {
private enum Constants { private enum Constants {
static let topAnchorInset: CGFloat = 20 static let topAnchorInset: CGFloat = 20
@ -28,16 +28,13 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
} }
} }
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
private let authenticationViewModel = AuthenticationViewModel() private let authenticationViewModel = AuthenticationViewModel()
private var authenticationStateTask: Task<(), Never>? private var authenticationStateTask: Task<(), Never>?
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var observations = Set<NSKeyValueObservation>() var observations = Set<NSKeyValueObservation>()
private(set) lazy var viewModel = WelcomeViewModel(context: context) private(set) lazy var viewModel = WelcomeViewModel()
let welcomeIllustrationView = WelcomeIllustrationView() let welcomeIllustrationView = WelcomeIllustrationView()
let separatorView = WelcomeSeparatorView(frame: .zero) let separatorView = WelcomeSeparatorView(frame: .zero)
@ -137,7 +134,7 @@ extension WelcomeViewController {
let alertController = UIAlertController(for: error, title: "Error", preferredStyle: .alert) let alertController = UIAlertController(for: error, title: "Error", preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil) let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction) alertController.addAction(okAction)
_ = self.coordinator.present( _ = self.sceneCoordinator?.present(
scene: .alertController(alertController: alertController), scene: .alertController(alertController: alertController),
from: nil, from: nil,
transition: .alertController(animated: true, completion: nil) transition: .alertController(animated: true, completion: nil)
@ -151,25 +148,25 @@ extension WelcomeViewController {
case .error(let error): case .error(let error):
displayError(error) displayError(error)
case .logInToExistingAccountRequested: case .logInToExistingAccountRequested:
_ = coordinator.present(scene: .mastodonLogin(authenticationViewModel: authenticationViewModel, suggestedDomain: viewModel.randomDefaultServer?.domain), from: self, transition: .show) _ = self.sceneCoordinator?.present(scene: .mastodonLogin(authenticationViewModel: authenticationViewModel, suggestedDomain: viewModel.randomDefaultServer?.domain), from: self, transition: .show)
case .joiningServer: case .joiningServer:
break break
case .showingRules(let viewModel): case .showingRules(let viewModel):
if let viewModel { if let viewModel {
_ = coordinator.present(scene: .mastodonServerRules(viewModel: viewModel), from: self, transition: .show) _ = self.sceneCoordinator?.present(scene: .mastodonServerRules(viewModel: viewModel), from: self, transition: .show)
} else { } else {
popBack() popBack()
} }
case .registering(let viewModel): case .registering(let viewModel):
_ = coordinator.present(scene: .mastodonRegister(viewModel: viewModel), from: self, transition: .show) _ = self.sceneCoordinator?.present(scene: .mastodonRegister(viewModel: viewModel), from: self, transition: .show)
case .showingPrivacyPolicy(let viewModel): case .showingPrivacyPolicy(let viewModel):
_ = coordinator.present(scene: .mastodonPrivacyPolicies(viewModel: viewModel), from: self, transition: .show) _ = self.sceneCoordinator?.present(scene: .mastodonPrivacyPolicies(viewModel: viewModel), from: self, transition: .show)
case .pickingServer: case .pickingServer:
_ = coordinator.present(scene: .mastodonPickServer(viewMode: MastodonPickServerViewModel(joinServer: { [weak self] server in try await self?.authenticationViewModel.joinServer(server) }, displayError: { [weak self] error in self?.displayError(error) })), from: self, transition: .show) _ = self.sceneCoordinator?.present(scene: .mastodonPickServer(viewMode: MastodonPickServerViewModel(joinServer: { [weak self] server in try await self?.authenticationViewModel.joinServer(server) }, displayError: { [weak self] error in self?.displayError(error) })), from: self, transition: .show)
case .confirmingEmail(let viewModel): case .confirmingEmail(let viewModel):
_ = coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show) _ = self.sceneCoordinator?.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show)
case .authenticatedUser(let authBox): case .authenticatedUser(let authBox):
coordinator.setup() self.sceneCoordinator?.setup()
break break
case .authenticatingUser: case .authenticatingUser:
break break

View File

@ -16,16 +16,11 @@ final class WelcomeViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
private(set) var defaultServers: [Mastodon.Entity.DefaultServer]? private(set) var defaultServers: [Mastodon.Entity.DefaultServer]?
var randomDefaultServer: Mastodon.Entity.Server? var randomDefaultServer: Mastodon.Entity.Server?
// input
let context: AppContext
// output // output
@Published var needsShowDismissEntry = false @Published var needsShowDismissEntry = false
init(context: AppContext) { init() {
self.context = context
AuthenticationServiceProvider.shared.$mastodonAuthenticationBoxes AuthenticationServiceProvider.shared.$mastodonAuthenticationBoxes
.map { !$0.isEmpty } .map { !$0.isEmpty }
.assign(to: &$needsShowDismissEntry) .assign(to: &$needsShowDismissEntry)

View File

@ -19,9 +19,6 @@ protocol ProfileAboutViewControllerDelegate: AnyObject {
final class ProfileAboutViewController: UIViewController { final class ProfileAboutViewController: UIViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
weak var delegate: ProfileAboutViewControllerDelegate? weak var delegate: ProfileAboutViewControllerDelegate?
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()

View File

@ -14,10 +14,7 @@ import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization import MastodonLocalization
final class BookmarkViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class BookmarkViewController: UIViewController, MediaPreviewableViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: BookmarkViewModel! var viewModel: BookmarkViewModel!

View File

@ -15,9 +15,7 @@ extension BookmarkViewModel {
) { ) {
diffableDataSource = StatusSection.diffableDataSource( diffableDataSource = StatusSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: StatusSection.Configuration( configuration: StatusSection.Configuration(
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
statusTableViewCellDelegate: statusTableViewCellDelegate, statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil, timelineMiddleLoaderTableViewCellDelegate: nil,

View File

@ -17,7 +17,6 @@ final class BookmarkViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let dataController: StatusDataController let dataController: StatusDataController
@ -38,8 +37,7 @@ final class BookmarkViewModel {
}() }()
@MainActor @MainActor
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.dataController = StatusDataController() self.dataController = StatusDataController()
} }

View File

@ -12,19 +12,14 @@ import MastodonLocalization
import MastodonUI import MastodonUI
import MastodonSDK import MastodonSDK
final class FamiliarFollowersViewController: UIViewController, NeedsDependency { final class FamiliarFollowersViewController: UIViewController {
weak var context: AppContext!
weak var coordinator: SceneCoordinator!
let viewModel: FamiliarFollowersViewModel let viewModel: FamiliarFollowersViewModel
let tableView: UITableView let tableView: UITableView
init(viewModel: FamiliarFollowersViewModel, context: AppContext, coordinator: SceneCoordinator) { init(viewModel: FamiliarFollowersViewModel) {
self.viewModel = viewModel self.viewModel = viewModel
self.context = context
self.coordinator = coordinator
tableView = UITableView() tableView = UITableView()
tableView.rowHeight = UITableView.automaticDimension tableView.rowHeight = UITableView.automaticDimension
tableView.separatorStyle = .none tableView.separatorStyle = .none

View File

@ -10,7 +10,6 @@ import MastodonCore
import MastodonSDK import MastodonSDK
final class FamiliarFollowersViewModel { final class FamiliarFollowersViewModel {
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
var accounts: [Mastodon.Entity.Account] var accounts: [Mastodon.Entity.Account]
@ -19,8 +18,7 @@ final class FamiliarFollowersViewModel {
// output // output
var diffableDataSource: UITableViewDiffableDataSource<UserSection, UserItem>? var diffableDataSource: UITableViewDiffableDataSource<UserSection, UserItem>?
init(context: AppContext, authenticationBox: MastodonAuthenticationBox, accounts: [Mastodon.Entity.Account], relationships: [Mastodon.Entity.Relationship]) { init(authenticationBox: MastodonAuthenticationBox, accounts: [Mastodon.Entity.Account], relationships: [Mastodon.Entity.Relationship]) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.accounts = accounts self.accounts = accounts
self.relationships = relationships self.relationships = relationships
@ -32,7 +30,6 @@ final class FamiliarFollowersViewModel {
) { ) {
diffableDataSource = UserSection.diffableDataSource( diffableDataSource = UserSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
userTableViewCellDelegate: userTableViewCellDelegate userTableViewCellDelegate: userTableViewCellDelegate
) )

View File

@ -17,10 +17,7 @@ import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization import MastodonLocalization
final class FavoriteViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class FavoriteViewController: UIViewController, MediaPreviewableViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: FavoriteViewModel! var viewModel: FavoriteViewModel!

View File

@ -15,9 +15,7 @@ extension FavoriteViewModel {
) { ) {
diffableDataSource = StatusSection.diffableDataSource( diffableDataSource = StatusSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: StatusSection.Configuration( configuration: StatusSection.Configuration(
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
statusTableViewCellDelegate: statusTableViewCellDelegate, statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil, timelineMiddleLoaderTableViewCellDelegate: nil,

View File

@ -17,7 +17,6 @@ final class FavoriteViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let dataController: StatusDataController let dataController: StatusDataController
@ -37,8 +36,7 @@ final class FavoriteViewModel {
}() }()
@MainActor @MainActor
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.dataController = StatusDataController() self.dataController = StatusDataController()
} }

View File

@ -11,9 +11,7 @@ import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization import MastodonLocalization
final class FollowedTagsViewController: UIViewController, NeedsDependency { final class FollowedTagsViewController: UIViewController {
var context: AppContext!
var coordinator: SceneCoordinator!
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
var viewModel: FollowedTagsViewModel var viewModel: FollowedTagsViewModel
@ -22,9 +20,7 @@ final class FollowedTagsViewController: UIViewController, NeedsDependency {
let tableView: UITableView let tableView: UITableView
let refreshControl: UIRefreshControl let refreshControl: UIRefreshControl
init(appContext: AppContext, sceneCoordinator: SceneCoordinator, authenticationBox: MastodonAuthenticationBox, viewModel: FollowedTagsViewModel) { init(authenticationBox: MastodonAuthenticationBox, viewModel: FollowedTagsViewModel) {
self.context = appContext
self.coordinator = sceneCoordinator
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.viewModel = viewModel self.viewModel = viewModel
@ -76,12 +72,11 @@ extension FollowedTagsViewController: UITableViewDelegate {
let object = viewModel.followedTags[indexPath.row] let object = viewModel.followedTags[indexPath.row]
let hashtagTimelineViewModel = HashtagTimelineViewModel( let hashtagTimelineViewModel = HashtagTimelineViewModel(
context: self.context,
authenticationBox: self.authenticationBox, authenticationBox: self.authenticationBox,
hashtag: object.name hashtag: object.name
) )
_ = self.coordinator.present( _ = self.sceneCoordinator?.present(
scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
from: self, from: self,
transition: .show transition: .show

View File

@ -16,11 +16,9 @@ final class FollowedTagsViewModel: NSObject {
var diffableDataSource: UITableViewDiffableDataSource<Section, Item>? var diffableDataSource: UITableViewDiffableDataSource<Section, Item>?
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { init(authenticationBox: MastodonAuthenticationBox) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.followedTags = [] self.followedTags = []

View File

@ -13,10 +13,7 @@ import MastodonUI
import MastodonLocalization import MastodonLocalization
import MastodonSDK import MastodonSDK
final class FollowerListViewController: UIViewController, NeedsDependency { final class FollowerListViewController: UIViewController {
weak var context: AppContext!
weak var coordinator: SceneCoordinator!
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: FollowerListViewModel var viewModel: FollowerListViewModel
@ -24,10 +21,8 @@ final class FollowerListViewController: UIViewController, NeedsDependency {
let tableView: UITableView let tableView: UITableView
let refreshControl: UIRefreshControl let refreshControl: UIRefreshControl
init(viewModel: FollowerListViewModel, coordinator: SceneCoordinator, context: AppContext) { init(viewModel: FollowerListViewModel) {
self.context = context
self.coordinator = coordinator
self.viewModel = viewModel self.viewModel = viewModel
tableView = UITableView() tableView = UITableView()

View File

@ -17,7 +17,6 @@ extension FollowerListViewModel {
) { ) {
diffableDataSource = UserSection.diffableDataSource( diffableDataSource = UserSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
userTableViewCellDelegate: userTableViewCellDelegate userTableViewCellDelegate: userTableViewCellDelegate
) )

View File

@ -15,7 +15,6 @@ final class FollowerListViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
@Published var accounts: [Mastodon.Entity.Account] @Published var accounts: [Mastodon.Entity.Account]
@Published var relationships: [Mastodon.Entity.Relationship] @Published var relationships: [Mastodon.Entity.Relationship]
@ -43,12 +42,10 @@ final class FollowerListViewModel {
}() }()
init( init(
context: AppContext,
authenticationBox: MastodonAuthenticationBox, authenticationBox: MastodonAuthenticationBox,
domain: String?, domain: String?,
userID: String? userID: String?
) { ) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.domain = domain self.domain = domain
self.userID = userID self.userID = userID

View File

@ -14,10 +14,7 @@ import MastodonUI
import CoreDataStack import CoreDataStack
import MastodonSDK import MastodonSDK
final class FollowingListViewController: UIViewController, NeedsDependency { final class FollowingListViewController: UIViewController {
weak var context: AppContext!
weak var coordinator: SceneCoordinator!
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: FollowingListViewModel var viewModel: FollowingListViewModel
@ -25,10 +22,8 @@ final class FollowingListViewController: UIViewController, NeedsDependency {
let refreshControl: UIRefreshControl let refreshControl: UIRefreshControl
let tableView: UITableView let tableView: UITableView
init(viewModel: FollowingListViewModel, coordinator: SceneCoordinator, context: AppContext) { init(viewModel: FollowingListViewModel) {
self.context = context
self.coordinator = coordinator
self.viewModel = viewModel self.viewModel = viewModel
tableView = UITableView() tableView = UITableView()

View File

@ -18,7 +18,6 @@ extension FollowingListViewModel {
) { ) {
diffableDataSource = UserSection.diffableDataSource( diffableDataSource = UserSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
userTableViewCellDelegate: userTableViewCellDelegate userTableViewCellDelegate: userTableViewCellDelegate
) )

View File

@ -16,7 +16,6 @@ final class FollowingListViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
@Published var accounts: [Mastodon.Entity.Account] @Published var accounts: [Mastodon.Entity.Account]
@Published var relationships: [Mastodon.Entity.Relationship] @Published var relationships: [Mastodon.Entity.Relationship]
@ -44,12 +43,10 @@ final class FollowingListViewModel {
}() }()
init( init(
context: AppContext,
authenticationBox: MastodonAuthenticationBox, authenticationBox: MastodonAuthenticationBox,
domain: String?, domain: String?,
userID: String? userID: String?
) { ) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.domain = domain self.domain = domain
self.userID = userID self.userID = userID

View File

@ -25,14 +25,11 @@ protocol ProfileHeaderViewControllerDelegate: AnyObject {
func profileHeaderViewController(_ profileHeaderViewController: ProfileHeaderViewController, profileHeaderView: ProfileHeaderView, metaTextView: MetaTextView, metaDidPressed meta: Meta) func profileHeaderViewController(_ profileHeaderViewController: ProfileHeaderViewController, profileHeaderView: ProfileHeaderView, metaTextView: MetaTextView, metaDidPressed meta: Meta)
} }
final class ProfileHeaderViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class ProfileHeaderViewController: UIViewController, MediaPreviewableViewController {
static let segmentedControlHeight: CGFloat = 50 static let segmentedControlHeight: CGFloat = 50
static let headerMinHeight: CGFloat = segmentedControlHeight static let headerMinHeight: CGFloat = segmentedControlHeight
weak var context: AppContext!
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
let viewModel: ProfileHeaderViewModel let viewModel: ProfileHeaderViewModel
@ -82,10 +79,8 @@ final class ProfileHeaderViewController: UIViewController, NeedsDependency, Medi
return documentPickerController return documentPickerController
}() }()
init(context: AppContext, authenticationBox: MastodonAuthenticationBox, coordinator: SceneCoordinator, profileViewModel: ProfileViewModel) { init(authenticationBox: MastodonAuthenticationBox, profileViewModel: ProfileViewModel) {
self.context = context self.viewModel = ProfileHeaderViewModel(authenticationBox: authenticationBox, account: profileViewModel.account, me: profileViewModel.me, relationship: profileViewModel.relationship)
self.coordinator = coordinator
self.viewModel = ProfileHeaderViewModel(context: context, authenticationBox: authenticationBox, account: profileViewModel.account, me: profileViewModel.me, relationship: profileViewModel.relationship)
self.profileHeaderView = ProfileHeaderView(account: profileViewModel.account, me: profileViewModel.me, relationship: profileViewModel.relationship) self.profileHeaderView = ProfileHeaderView(account: profileViewModel.account, me: profileViewModel.me, relationship: profileViewModel.relationship)
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
@ -341,12 +336,11 @@ extension ProfileHeaderViewController: ProfileHeaderViewDelegate {
guard let domain = viewModel.account.domain else { return } guard let domain = viewModel.account.domain else { return }
let userID = viewModel.account.id let userID = viewModel.account.id
let followerListViewModel = FollowerListViewModel( let followerListViewModel = FollowerListViewModel(
context: context,
authenticationBox: viewModel.authenticationBox, authenticationBox: viewModel.authenticationBox,
domain: domain, domain: domain,
userID: userID userID: userID
) )
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .follower(viewModel: followerListViewModel), scene: .follower(viewModel: followerListViewModel),
from: self, from: self,
transition: .show transition: .show
@ -357,12 +351,11 @@ extension ProfileHeaderViewController: ProfileHeaderViewDelegate {
let userID = viewModel.account.id let userID = viewModel.account.id
let followingListViewModel = FollowingListViewModel( let followingListViewModel = FollowingListViewModel(
context: context,
authenticationBox: viewModel.authenticationBox, authenticationBox: viewModel.authenticationBox,
domain: domain, domain: domain,
userID: userID userID: userID
) )
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .following(viewModel: followingListViewModel), scene: .following(viewModel: followingListViewModel),
from: self, from: self,
transition: .show transition: .show

View File

@ -23,7 +23,6 @@ final class ProfileHeaderViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
@Published var me: Mastodon.Entity.Account @Published var me: Mastodon.Entity.Account
@ -45,8 +44,7 @@ final class ProfileHeaderViewModel {
@Published var isTitleViewDisplaying = false @Published var isTitleViewDisplaying = false
@Published var isTitleViewContentOffsetSet = false @Published var isTitleViewContentOffsetSet = false
init(context: AppContext, authenticationBox: MastodonAuthenticationBox, account: Mastodon.Entity.Account, me: Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?) { init(authenticationBox: MastodonAuthenticationBox, account: Mastodon.Entity.Account, me: Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.account = account self.account = account
self.me = me self.me = me

View File

@ -22,14 +22,11 @@ protocol ProfileViewModelEditable {
var isEdited: Bool { get } var isEdited: Bool { get }
} }
final class ProfileViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { final class ProfileViewController: UIViewController, MediaPreviewableViewController {
public static let containerViewMarginForRegularHorizontalSizeClass: CGFloat = 64 public static let containerViewMarginForRegularHorizontalSizeClass: CGFloat = 64
public static let containerViewMarginForCompactHorizontalSizeClass: CGFloat = 16 public static let containerViewMarginForCompactHorizontalSizeClass: CGFloat = 16
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: ProfileViewModel? { var viewModel: ProfileViewModel? {
@ -144,7 +141,7 @@ final class ProfileViewController: UIViewController, NeedsDependency, MediaPrevi
} }
private func createProfileHeaderViewController(viewModel: ProfileViewModel) -> ProfileHeaderViewController { private func createProfileHeaderViewController(viewModel: ProfileViewModel) -> ProfileHeaderViewController {
let viewController = ProfileHeaderViewController(context: context, authenticationBox: viewModel.authenticationBox, coordinator: coordinator, profileViewModel: viewModel) let viewController = ProfileHeaderViewController(authenticationBox: viewModel.authenticationBox, profileViewModel: viewModel)
return viewController return viewController
} }
@ -159,12 +156,6 @@ final class ProfileViewController: UIViewController, NeedsDependency, MediaPrevi
mediaUserTimelineViewModel: viewModel.mediaUserTimelineViewModel, mediaUserTimelineViewModel: viewModel.mediaUserTimelineViewModel,
profileAboutViewModel: viewModel.profileAboutViewModel profileAboutViewModel: viewModel.profileAboutViewModel
) )
profilePagingViewModel.viewControllers.forEach { viewController in
if let viewController = viewController as? NeedsDependency {
viewController.context = context
viewController.coordinator = coordinator
}
}
return profilePagingViewModel return profilePagingViewModel
}() }()
return profilePagingViewController return profilePagingViewController
@ -553,15 +544,15 @@ extension ProfileViewController {
switch meta { switch meta {
case .url(_, _, let url, _): case .url(_, _, let url, _):
guard let url = URL(string: url) else { return } guard let url = URL(string: url) else { return }
_ = coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
case .mention(_, _, let userInfo): case .mention(_, _, let userInfo):
guard let href = userInfo?["href"] as? String, guard let href = userInfo?["href"] as? String,
let url = URL(string: href) else { return } let url = URL(string: href) else { return }
_ = coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
case .hashtag(_, let hashtag, _): case .hashtag(_, let hashtag, _):
guard let viewModel = viewModel else { break } guard let viewModel = viewModel else { break }
let hashtagTimelineViewModel = HashtagTimelineViewModel(context: context, authenticationBox: viewModel.authenticationBox, hashtag: hashtag) let hashtagTimelineViewModel = HashtagTimelineViewModel(authenticationBox: viewModel.authenticationBox, hashtag: hashtag)
_ = coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: nil, transition: .show) _ = self.sceneCoordinator?.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: nil, transition: .show)
case .email, .emoji: case .email, .emoji:
break break
} }
@ -578,7 +569,7 @@ extension ProfileViewController {
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) { @objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
guard let setting = SettingService.shared.currentSetting.value else { return } guard let setting = SettingService.shared.currentSetting.value else { return }
_ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none) _ = self.sceneCoordinator?.present(scene: .settings(setting: setting), from: self, transition: .none)
} }
@objc private func shareBarButtonItemPressed(_ sender: UIBarButtonItem) { @objc private func shareBarButtonItemPressed(_ sender: UIBarButtonItem) {
@ -588,7 +579,7 @@ extension ProfileViewController {
dependency: self, dependency: self,
account: viewModel.account account: viewModel.account
) )
_ = self.coordinator.present( _ = self.sceneCoordinator?.present(
scene: .activityViewController( scene: .activityViewController(
activityViewController: activityViewController, activityViewController: activityViewController,
sourceView: nil, sourceView: nil,
@ -602,15 +593,15 @@ extension ProfileViewController {
@objc private func favoriteBarButtonItemPressed(_ sender: UIBarButtonItem) { @objc private func favoriteBarButtonItemPressed(_ sender: UIBarButtonItem) {
guard let viewModel = viewModel else { return } guard let viewModel = viewModel else { return }
let favoriteViewModel = FavoriteViewModel(context: context, authenticationBox: viewModel.authenticationBox) let favoriteViewModel = FavoriteViewModel(authenticationBox: viewModel.authenticationBox)
_ = coordinator.present(scene: .favorite(viewModel: favoriteViewModel), from: self, transition: .show) _ = self.sceneCoordinator?.present(scene: .favorite(viewModel: favoriteViewModel), from: self, transition: .show)
} }
@objc private func bookmarkBarButtonItemPressed(_ sender: UIBarButtonItem) { @objc private func bookmarkBarButtonItemPressed(_ sender: UIBarButtonItem) {
guard let viewModel = viewModel else { return } guard let viewModel = viewModel else { return }
let bookmarkViewModel = BookmarkViewModel(context: context, authenticationBox: viewModel.authenticationBox) let bookmarkViewModel = BookmarkViewModel(authenticationBox: viewModel.authenticationBox)
_ = coordinator.present(scene: .bookmark(viewModel: bookmarkViewModel), from: self, transition: .show) _ = self.sceneCoordinator?.present(scene: .bookmark(viewModel: bookmarkViewModel), from: self, transition: .show)
} }
@objc private func replyBarButtonItemPressed(_ sender: UIBarButtonItem) { @objc private func replyBarButtonItemPressed(_ sender: UIBarButtonItem) {
@ -619,20 +610,19 @@ extension ProfileViewController {
let mention = "@" + viewModel.account.acct let mention = "@" + viewModel.account.acct
UITextChecker.learnWord(mention) UITextChecker.learnWord(mention)
let composeViewModel = ComposeViewModel( let composeViewModel = ComposeViewModel(
context: context,
authenticationBox: viewModel.authenticationBox, authenticationBox: viewModel.authenticationBox,
composeContext: .composeStatus, composeContext: .composeStatus,
destination: .topLevel, destination: .topLevel,
initialContent: mention initialContent: mention
) )
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil)) _ = self.sceneCoordinator?.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
} }
@objc private func followedTagsItemPressed(_ sender: UIBarButtonItem) { @objc private func followedTagsItemPressed(_ sender: UIBarButtonItem) {
guard let viewModel = viewModel else { return } guard let viewModel = viewModel else { return }
let followedTagsViewModel = FollowedTagsViewModel(context: context, authenticationBox: viewModel.authenticationBox) let followedTagsViewModel = FollowedTagsViewModel(authenticationBox: viewModel.authenticationBox)
_ = coordinator.present(scene: .followedTags(viewModel: followedTagsViewModel), from: self, transition: .show) _ = self.sceneCoordinator?.present(scene: .followedTags(viewModel: followedTagsViewModel), from: self, transition: .show)
} }
@objc private func refreshControlValueChanged(_ sender: RefreshControl) { @objc private func refreshControlValueChanged(_ sender: RefreshControl) {
@ -840,7 +830,7 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.EditProfileFailure.title, preferredStyle: .alert) let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.EditProfileFailure.title, preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default)
alertController.addAction(okAction) alertController.addAction(okAction)
_ = self.coordinator.present( _ = self.sceneCoordinator?.present(
scene: .alertController(alertController: alertController), scene: .alertController(alertController: alertController),
from: nil, from: nil,
transition: .alertController(animated: true, completion: nil) transition: .alertController(animated: true, completion: nil)
@ -900,7 +890,7 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
alertController.addAction(unblockAction) alertController.addAction(unblockAction)
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel) let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel)
alertController.addAction(cancelAction) alertController.addAction(cancelAction)
coordinator.present(scene: .alertController(alertController: alertController), transition: .alertController(animated: true)) self.sceneCoordinator?.present(scene: .alertController(alertController: alertController), transition: .alertController(animated: true))
} else if relationship.domainBlocking { } else if relationship.domainBlocking {
guard let domain = account.domain else { return } guard let domain = account.domain else { return }
@ -930,7 +920,7 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
alertController.addAction(unblockAction) alertController.addAction(unblockAction)
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel) let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel)
alertController.addAction(cancelAction) alertController.addAction(cancelAction)
coordinator.present(scene: .alertController(alertController: alertController), transition: .alertController(animated: true)) self.sceneCoordinator?.present(scene: .alertController(alertController: alertController), transition: .alertController(animated: true))
} else if relationship.muting { } else if relationship.muting {
let name = account.displayNameWithFallback let name = account.displayNameWithFallback
@ -950,7 +940,7 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
alertController.addAction(unmuteAction) alertController.addAction(unmuteAction)
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel) let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel)
alertController.addAction(cancelAction) alertController.addAction(cancelAction)
coordinator.present(scene: .alertController(alertController: alertController), transition: .alertController(animated: true)) self.sceneCoordinator?.present(scene: .alertController(alertController: alertController), transition: .alertController(animated: true))
} else { } else {
Task { [weak self] in Task { [weak self] in
guard let self else { return } guard let self else { return }

View File

@ -14,10 +14,7 @@ import TabBarPager
import XLPagerTabStrip import XLPagerTabStrip
import MastodonCore import MastodonCore
final class UserTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController, StatusReloadable { final class UserTimelineViewController: UIViewController, MediaPreviewableViewController, StatusReloadable {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: UserTimelineViewModel! var viewModel: UserTimelineViewModel!

View File

@ -16,9 +16,7 @@ extension UserTimelineViewModel {
) { ) {
diffableDataSource = StatusSection.diffableDataSource( diffableDataSource = StatusSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
configuration: StatusSection.Configuration( configuration: StatusSection.Configuration(
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
statusTableViewCellDelegate: statusTableViewCellDelegate, statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil, timelineMiddleLoaderTableViewCellDelegate: nil,

View File

@ -13,9 +13,7 @@ import MastodonLocalization
import MastodonUI import MastodonUI
import CoreDataStack import CoreDataStack
final class FavoritedByViewController: UIViewController, NeedsDependency { final class FavoritedByViewController: UIViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: UserListViewModel! var viewModel: UserListViewModel!

View File

@ -13,10 +13,7 @@ import MastodonLocalization
import MastodonUI import MastodonUI
import CoreDataStack import CoreDataStack
final class RebloggedByViewController: UIViewController, NeedsDependency { final class RebloggedByViewController: UIViewController {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
var viewModel: UserListViewModel! var viewModel: UserListViewModel!

View File

@ -19,7 +19,6 @@ extension UserListViewModel {
) { ) {
diffableDataSource = UserSection.diffableDataSource( diffableDataSource = UserSection.diffableDataSource(
tableView: tableView, tableView: tableView,
context: context,
authenticationBox: authenticationBox, authenticationBox: authenticationBox,
userTableViewCellDelegate: userTableViewCellDelegate userTableViewCellDelegate: userTableViewCellDelegate
) )

View File

@ -12,13 +12,10 @@ import MastodonAsset
import MastodonCore import MastodonCore
import MastodonLocalization import MastodonLocalization
class ReportViewController: UIViewController, NeedsDependency, ReportViewControllerAppearance { class ReportViewController: UIViewController, ReportViewControllerAppearance {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
private var observations = Set<NSKeyValueObservation>() private var observations = Set<NSKeyValueObservation>()
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
let viewModel: ReportViewModel let viewModel: ReportViewModel
@ -50,8 +47,6 @@ class ReportViewController: UIViewController, NeedsDependency, ReportViewControl
viewModel.reportSupplementaryViewModel.delegate = self viewModel.reportSupplementaryViewModel.delegate = self
let reportReasonViewController = ReportReasonViewController(viewModel: viewModel.reportReasonViewModel) let reportReasonViewController = ReportReasonViewController(viewModel: viewModel.reportReasonViewModel)
reportReasonViewController.context = context
reportReasonViewController.coordinator = coordinator
addChild(reportReasonViewController) addChild(reportReasonViewController)
reportReasonViewController.view.translatesAutoresizingMaskIntoConstraints = false reportReasonViewController.view.translatesAutoresizingMaskIntoConstraints = false
@ -80,25 +75,24 @@ extension ReportViewController: ReportReasonViewControllerDelegate {
switch reason { switch reason {
case .dislike: case .dislike:
let reportResultViewModel = ReportResultViewModel( let reportResultViewModel = ReportResultViewModel(
context: context,
authenticationBox: viewModel.authenticationBox, authenticationBox: viewModel.authenticationBox,
account: viewModel.account, account: viewModel.account,
relationship: viewModel.relationship, relationship: viewModel.relationship,
isReported: false isReported: false
) )
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .reportResult(viewModel: reportResultViewModel), scene: .reportResult(viewModel: reportResultViewModel),
from: self, from: self,
transition: .show transition: .show
) )
case .violateRule: case .violateRule:
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .reportServerRules(viewModel: viewModel.reportServerRulesViewModel), scene: .reportServerRules(viewModel: viewModel.reportServerRulesViewModel),
from: self, from: self,
transition: .show transition: .show
) )
case .spam, .other: case .spam, .other:
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .reportStatus(viewModel: viewModel.reportStatusViewModel), scene: .reportStatus(viewModel: viewModel.reportStatusViewModel),
from: self, from: self,
transition: .show transition: .show
@ -114,7 +108,7 @@ extension ReportViewController: ReportServerRulesViewControllerDelegate {
return return
} }
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .reportStatus(viewModel: viewModel.reportStatusViewModel), scene: .reportStatus(viewModel: viewModel.reportStatusViewModel),
from: self, from: self,
transition: .show transition: .show
@ -133,7 +127,7 @@ extension ReportViewController: ReportStatusViewControllerDelegate {
} }
private func coordinateToReportSupplementary() { private func coordinateToReportSupplementary() {
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .reportSupplementary(viewModel: viewModel.reportSupplementaryViewModel), scene: .reportSupplementary(viewModel: viewModel.reportSupplementaryViewModel),
from: self, from: self,
transition: .show transition: .show
@ -157,14 +151,13 @@ extension ReportViewController: ReportSupplementaryViewControllerDelegate {
let _ = try await viewModel.report() let _ = try await viewModel.report()
let reportResultViewModel = ReportResultViewModel( let reportResultViewModel = ReportResultViewModel(
context: context,
authenticationBox: viewModel.authenticationBox, authenticationBox: viewModel.authenticationBox,
account: viewModel.account, account: viewModel.account,
relationship: viewModel.relationship, relationship: viewModel.relationship,
isReported: true isReported: true
) )
_ = coordinator.present( _ = self.sceneCoordinator?.present(
scene: .reportResult(viewModel: reportResultViewModel), scene: .reportResult(viewModel: reportResultViewModel),
from: self, from: self,
transition: .show transition: .show
@ -174,7 +167,7 @@ extension ReportViewController: ReportSupplementaryViewControllerDelegate {
let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert) let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil) let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction) alertController.addAction(okAction)
_ = self.coordinator.present( _ = self.sceneCoordinator?.present(
scene: .alertController(alertController: alertController), scene: .alertController(alertController: alertController),
from: nil, from: nil,
transition: .alertController(animated: true, completion: nil) transition: .alertController(animated: true, completion: nil)

View File

@ -17,10 +17,7 @@ protocol ReportReasonViewControllerDelegate: AnyObject {
func reportReasonViewController(_ viewController: ReportReasonViewController, nextButtonPressed button: UIButton) func reportReasonViewController(_ viewController: ReportReasonViewController, nextButtonPressed button: UIButton)
} }
final class ReportReasonViewController: UIViewController, NeedsDependency, ReportViewControllerAppearance { final class ReportReasonViewController: UIViewController, ReportViewControllerAppearance {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
private var observations = Set<NSKeyValueObservation>() private var observations = Set<NSKeyValueObservation>()

View File

@ -12,13 +12,10 @@ import MastodonAsset
import MastodonCore import MastodonCore
import MastodonLocalization import MastodonLocalization
final class ReportResultViewController: UIViewController, NeedsDependency, ReportViewControllerAppearance { final class ReportResultViewController: UIViewController, ReportViewControllerAppearance {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
private var observations = Set<NSKeyValueObservation>() private var observations = Set<NSKeyValueObservation>()
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var viewModel: ReportResultViewModel! var viewModel: ReportResultViewModel!
private(set) lazy var reportResultView = ReportResultView(viewModel: viewModel) private(set) lazy var reportResultView = ReportResultView(viewModel: viewModel)

View File

@ -21,7 +21,6 @@ class ReportResultViewModel: ObservableObject {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
// input // input
let context: AppContext
let authenticationBox: MastodonAuthenticationBox let authenticationBox: MastodonAuthenticationBox
let account: Mastodon.Entity.Account let account: Mastodon.Entity.Account
var relationship: Mastodon.Entity.Relationship var relationship: Mastodon.Entity.Relationship
@ -46,13 +45,11 @@ class ReportResultViewModel: ObservableObject {
let blockActionPublisher = PassthroughSubject<Void, Never>() let blockActionPublisher = PassthroughSubject<Void, Never>()
init( init(
context: AppContext,
authenticationBox: MastodonAuthenticationBox, authenticationBox: MastodonAuthenticationBox,
account: Mastodon.Entity.Account, account: Mastodon.Entity.Account,
relationship: Mastodon.Entity.Relationship, relationship: Mastodon.Entity.Relationship,
isReported: Bool isReported: Bool
) { ) {
self.context = context
self.authenticationBox = authenticationBox self.authenticationBox = authenticationBox
self.account = account self.account = account
self.relationship = relationship self.relationship = relationship

Some files were not shown because too many files have changed in this diff Show More