mirror of
https://github.com/mastodon/mastodon-ios
synced 2025-04-11 22:58:02 +02:00
Fix crash on launch due to infinite loop
And more honestly about singletons.
This commit is contained in:
parent
77f3c5a64d
commit
c4442fe8a9
@ -46,7 +46,7 @@ final public class SceneCoordinator {
|
||||
|
||||
scene.session.sceneCoordinator = self
|
||||
|
||||
appContext.notificationService.requestRevealNotificationPublisher
|
||||
NotificationService.shared.requestRevealNotificationPublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink(receiveValue: {
|
||||
[weak self] pushNotification in
|
||||
@ -98,12 +98,12 @@ final public class SceneCoordinator {
|
||||
|
||||
switch type {
|
||||
case .follow:
|
||||
let account = try await appContext.apiService.notification(
|
||||
let account = try await APIService.shared.notification(
|
||||
notificationID: notificationID,
|
||||
authenticationBox: authenticationBox
|
||||
).value.account
|
||||
|
||||
let relationship = try await appContext.apiService.relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first
|
||||
let relationship = try await APIService.shared.relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first
|
||||
|
||||
let profileViewModel = ProfileViewModel(
|
||||
context: appContext,
|
||||
@ -645,7 +645,7 @@ extension SceneCoordinator: SettingsCoordinatorDelegate {
|
||||
let signOutAction = UIAlertAction(title: L10n.Common.Alerts.SignOut.confirm, style: .destructive) { [weak self] _ in
|
||||
guard let self, let authenticationBox = self.authenticationBox else { return }
|
||||
|
||||
self.appContext.notificationService.clearNotificationCountForActiveUser()
|
||||
NotificationService.shared.clearNotificationCountForActiveUser()
|
||||
|
||||
Task { @MainActor in
|
||||
try await AuthenticationServiceProvider.shared.signOutMastodonUser(
|
||||
|
@ -17,7 +17,7 @@ extension DataSourceFacade {
|
||||
) async throws -> Mastodon.Entity.Relationship {
|
||||
FeedbackGenerator.shared.generate(.selectionChanged)
|
||||
|
||||
let apiService = dependency.context.apiService
|
||||
let apiService = APIService.shared
|
||||
let authBox = dependency.authenticationBox
|
||||
|
||||
let response = try await apiService.toggleBlock(
|
||||
@ -40,7 +40,7 @@ extension DataSourceFacade {
|
||||
) async throws -> Mastodon.Entity.Empty {
|
||||
FeedbackGenerator.shared.generate(.selectionChanged)
|
||||
|
||||
let apiService = dependency.context.apiService
|
||||
let apiService = APIService.shared
|
||||
let authBox = dependency.authenticationBox
|
||||
|
||||
let response = try await apiService.toggleDomainBlock(account: account, authenticationBox: authBox)
|
||||
|
@ -19,7 +19,7 @@ extension DataSourceFacade {
|
||||
) async throws {
|
||||
FeedbackGenerator.shared.generate(.selectionChanged)
|
||||
|
||||
let updatedStatus = try await provider.context.apiService.bookmark(
|
||||
let updatedStatus = try await APIService.shared.bookmark(
|
||||
record: status,
|
||||
authenticationBox: provider.authenticationBox
|
||||
).value
|
||||
|
@ -18,7 +18,7 @@ extension DataSourceFacade {
|
||||
) async throws {
|
||||
FeedbackGenerator.shared.generate(.selectionChanged)
|
||||
|
||||
let updatedStatus = try await provider.context.apiService.favorite(
|
||||
let updatedStatus = try await APIService.shared.favorite(
|
||||
status: status,
|
||||
authenticationBox: provider.authenticationBox
|
||||
).value
|
||||
|
@ -18,7 +18,7 @@ extension DataSourceFacade {
|
||||
account: Mastodon.Entity.Account
|
||||
) async throws -> Mastodon.Entity.Relationship {
|
||||
let authBox = dependency.authenticationBox
|
||||
let relationship = try await dependency.context.apiService.relationship(
|
||||
let relationship = try await APIService.shared.relationship(
|
||||
forAccounts: [account], authenticationBox: authBox
|
||||
).value.first
|
||||
|
||||
@ -27,7 +27,7 @@ extension DataSourceFacade {
|
||||
let performAction = {
|
||||
FeedbackGenerator.shared.generate(.selectionChanged)
|
||||
|
||||
let response = try await dependency.context.apiService.toggleFollow(
|
||||
let response = try await APIService.shared.toggleFollow(
|
||||
account: account,
|
||||
authenticationBox: dependency.authenticationBox
|
||||
).value
|
||||
@ -100,7 +100,7 @@ extension DataSourceFacade {
|
||||
await notificationView.configure(notification: notification, authenticationBox: dependency.authenticationBox)
|
||||
|
||||
do {
|
||||
let newRelationship = try await dependency.context.apiService.followRequest(
|
||||
let newRelationship = try await APIService.shared.followRequest(
|
||||
userID: userID,
|
||||
query: query,
|
||||
authenticationBox: dependency.authenticationBox
|
||||
@ -149,7 +149,7 @@ extension DataSourceFacade {
|
||||
dependency: NeedsDependency & AuthContextProvider,
|
||||
account: Mastodon.Entity.Account
|
||||
) async throws {
|
||||
let newRelationship = try await dependency.context.apiService.toggleShowReblogs(
|
||||
let newRelationship = try await APIService.shared.toggleShowReblogs(
|
||||
for: account,
|
||||
authenticationBox: dependency.authenticationBox
|
||||
)
|
||||
|
@ -16,7 +16,7 @@ extension DataSourceFacade {
|
||||
) async throws -> Mastodon.Entity.Relationship {
|
||||
FeedbackGenerator.shared.generate(.selectionChanged)
|
||||
|
||||
let response = try await dependency.context.apiService.toggleMute(
|
||||
let response = try await APIService.shared.toggleMute(
|
||||
authenticationBox: dependency.authenticationBox,
|
||||
account: account
|
||||
)
|
||||
|
@ -12,7 +12,7 @@ extension DataSourceFacade {
|
||||
provider.coordinator.showLoading()
|
||||
|
||||
do {
|
||||
let notificationRequests = try await provider.context.apiService.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)
|
||||
|
||||
provider.coordinator.hideLoading()
|
||||
|
@ -57,7 +57,7 @@ extension DataSourceFacade {
|
||||
provider.coordinator.showLoading()
|
||||
|
||||
do {
|
||||
guard let account = try await provider.context.apiService.fetchUser(
|
||||
guard let account = try await APIService.shared.fetchUser(
|
||||
username: username,
|
||||
domain: domain,
|
||||
authenticationBox: provider.authenticationBox
|
||||
@ -82,7 +82,7 @@ extension DataSourceFacade {
|
||||
provider.coordinator.showLoading()
|
||||
|
||||
do {
|
||||
let account = try await provider.context.apiService.accountInfo(
|
||||
let account = try await APIService.shared.accountInfo(
|
||||
domain: domain,
|
||||
userID: accountID,
|
||||
authorization: provider.authenticationBox.userAuthorization
|
||||
@ -104,7 +104,7 @@ extension DataSourceFacade {
|
||||
provider.coordinator.showLoading()
|
||||
|
||||
guard let me = provider.authenticationBox.authentication.account(),
|
||||
let relationship = try? await provider.context.apiService.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()
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ private extension DataSourceFacade {
|
||||
) async throws {
|
||||
FeedbackGenerator.shared.generate(.selectionChanged)
|
||||
|
||||
let updatedStatus = try await provider.context.apiService.reblog(
|
||||
let updatedStatus = try await APIService.shared.reblog(
|
||||
status: status,
|
||||
authenticationBox: provider.authenticationBox
|
||||
).value
|
||||
|
@ -10,7 +10,7 @@ extension DataSourceFacade {
|
||||
forStatus status: Status,
|
||||
provider: NeedsDependency & AuthContextProvider
|
||||
) async throws -> [Mastodon.Entity.StatusEdit] {
|
||||
let reponse = try await provider.context.apiService.getHistory(forStatusID: status.id, authenticationBox: provider.authenticationBox)
|
||||
let reponse = try await APIService.shared.getHistory(forStatusID: status.id, authenticationBox: provider.authenticationBox)
|
||||
|
||||
return reponse.value
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ extension DataSourceFacade {
|
||||
dependency: NeedsDependency & AuthContextProvider & DataSourceProvider,
|
||||
status: MastodonStatus
|
||||
) async throws {
|
||||
let deletedStatus = try await dependency.context.apiService.deleteStatus(
|
||||
let deletedStatus = try await APIService.shared.deleteStatus(
|
||||
status: status,
|
||||
authenticationBox: dependency.authenticationBox
|
||||
).value.asMastodonStatus
|
||||
@ -234,7 +234,7 @@ extension DataSourceFacade {
|
||||
alertController.addAction(cancelAction)
|
||||
dependency.present(alertController, animated: true)
|
||||
case .reportUser:
|
||||
guard let relationship = try? await dependency.context.apiService.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(
|
||||
context: dependency.context,
|
||||
@ -333,7 +333,7 @@ extension DataSourceFacade {
|
||||
|
||||
guard let status = menuContext.statusViewModel?.originalStatus else { return }
|
||||
|
||||
let statusSource = try await dependency.context.apiService.getStatusSource(
|
||||
let statusSource = try await APIService.shared.getStatusSource(
|
||||
forStatusID: status.id,
|
||||
authenticationBox: dependency.authenticationBox
|
||||
).value
|
||||
|
@ -25,8 +25,7 @@ extension DataSourceFacade {
|
||||
FeedbackGenerator.shared.generate(.selectionChanged)
|
||||
|
||||
do {
|
||||
let value = try await provider.context
|
||||
.apiService
|
||||
let value = try await APIService.shared
|
||||
.translateStatus(
|
||||
statusID: status.id,
|
||||
authenticationBox: provider.authenticationBox
|
||||
|
@ -568,7 +568,7 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider & Aut
|
||||
.compactMap { poll.options.firstIndex(of: $0) }
|
||||
|
||||
do {
|
||||
let newPoll = try await context.apiService.vote(
|
||||
let newPoll = try await APIService.shared.vote(
|
||||
poll: poll.entity,
|
||||
choices: choices,
|
||||
authenticationBox: authenticationBox
|
||||
|
@ -310,7 +310,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
|
||||
.compactMap { poll.options.firstIndex(of: $0) }
|
||||
|
||||
do {
|
||||
let newPoll = try await context.apiService.vote(
|
||||
let newPoll = try await APIService.shared.vote(
|
||||
poll: poll.entity,
|
||||
choices: choices,
|
||||
authenticationBox: authenticationBox
|
||||
@ -584,7 +584,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
|
||||
}
|
||||
|
||||
do {
|
||||
let edits = try await context.apiService.getHistory(forStatusID: status.id, authenticationBox: authenticationBox).value
|
||||
let edits = try await APIService.shared.getHistory(forStatusID: status.id, authenticationBox: authenticationBox).value
|
||||
|
||||
await coordinator.hideLoading()
|
||||
|
||||
|
@ -124,7 +124,7 @@ extension UITableViewDelegate where Self: DataSourceProvider & MediaPreviewableV
|
||||
guard let self = self else { return }
|
||||
Task { @MainActor in
|
||||
do {
|
||||
try await self.context.photoLibraryService.save(
|
||||
try await PhotoLibraryService.shared.save(
|
||||
imageSource: .url(assetURL)
|
||||
).singleOutput()
|
||||
} catch {
|
||||
@ -153,7 +153,7 @@ extension UITableViewDelegate where Self: DataSourceProvider & MediaPreviewableV
|
||||
) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
Task {
|
||||
try await self.context.photoLibraryService.copy(
|
||||
try await PhotoLibraryService.shared.copy(
|
||||
imageSource: .url(assetURL)
|
||||
).singleOutput()
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ extension ComposeViewController {
|
||||
private func enqueuePublishStatus() {
|
||||
do {
|
||||
let statusPublisher = try composeContentViewModel.statusPublisher()
|
||||
viewModel.context.publisherService.enqueue(
|
||||
PublisherService.shared.enqueue(
|
||||
statusPublisher: statusPublisher,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
@ -294,7 +294,7 @@ extension ComposeViewController {
|
||||
private func enqueuePublishStatusEdit() {
|
||||
do {
|
||||
guard let editStatusPublisher = try composeContentViewModel.statusEditPublisher() else { return }
|
||||
viewModel.context.publisherService.enqueue(
|
||||
PublisherService.shared.enqueue(
|
||||
statusPublisher: editStatusPublisher,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
@ -325,7 +325,6 @@ extension ComposeViewController {
|
||||
if UIPasteboard.general.hasImages, let images = UIPasteboard.general.images {
|
||||
let attachmentViewModels = images.map { image in
|
||||
return AttachmentViewModel(
|
||||
api: viewModel.context.apiService,
|
||||
authenticationBox: viewModel.authenticationBox,
|
||||
input: .image(image),
|
||||
sizeLimit: composeContentViewModel.sizeLimit,
|
||||
|
@ -145,7 +145,7 @@ extension DiscoveryForYouViewController: ProfileCardTableViewCellDelegate {
|
||||
do {
|
||||
let userID = account.id
|
||||
let familiarFollowers = viewModel.familiarFollowers.first(where: { $0.id == userID })?.accounts ?? []
|
||||
let relationships = try await context.apiService.relationship(forAccounts: familiarFollowers, authenticationBox: authenticationBox).value
|
||||
let relationships = try await APIService.shared.relationship(forAccounts: familiarFollowers, authenticationBox: authenticationBox).value
|
||||
|
||||
coordinator.hideLoading()
|
||||
|
||||
|
@ -48,12 +48,12 @@ extension DiscoveryForYouViewModel {
|
||||
do {
|
||||
let suggestedAccounts = try await fetchSuggestionAccounts()
|
||||
|
||||
let familiarFollowersResponse = try? await context.apiService.familiarFollowers(
|
||||
let familiarFollowersResponse = try? await APIService.shared.familiarFollowers(
|
||||
query: .init(ids: suggestedAccounts.compactMap { $0.id }),
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
|
||||
let relationships = try? await context.apiService.relationship(
|
||||
let relationships = try? await APIService.shared.relationship(
|
||||
forAccounts: suggestedAccounts,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
@ -85,14 +85,14 @@ extension DiscoveryForYouViewModel {
|
||||
|
||||
private func fetchSuggestionAccounts() async throws -> [Mastodon.Entity.Account] {
|
||||
do {
|
||||
let response = try await context.apiService.suggestionAccountV2(
|
||||
let response = try await APIService.shared.suggestionAccountV2(
|
||||
query: nil,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
return response.compactMap { $0.account }
|
||||
} catch {
|
||||
// fallback V1
|
||||
let response = try await context.apiService.suggestionAccount(
|
||||
let response = try await APIService.shared.suggestionAccount(
|
||||
query: nil,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
|
@ -35,7 +35,7 @@ final class DiscoveryHashtagsViewModel {
|
||||
.throttle(for: 3, scheduler: DispatchQueue.main, latest: true)
|
||||
.asyncMap { _ in
|
||||
let authenticationBox = authenticationBox
|
||||
return try await context.apiService.trendHashtags(domain: authenticationBox.domain,
|
||||
return try await APIService.shared.trendHashtags(domain: authenticationBox.domain,
|
||||
query: nil,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
@ -63,7 +63,7 @@ extension DiscoveryHashtagsViewModel {
|
||||
func fetch() async throws {
|
||||
|
||||
let authenticationBox = authenticationBox
|
||||
let response = try await context.apiService.trendHashtags(domain: authenticationBox.domain,
|
||||
let response = try await APIService.shared.trendHashtags(domain: authenticationBox.domain,
|
||||
query: nil,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
|
@ -8,6 +8,7 @@
|
||||
import Foundation
|
||||
import GameplayKit
|
||||
import MastodonSDK
|
||||
import MastodonCore
|
||||
|
||||
extension DiscoveryNewsViewModel {
|
||||
class State: GKState {
|
||||
@ -123,7 +124,7 @@ extension DiscoveryNewsViewModel.State {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.trendLinks(
|
||||
let response = try await APIService.shared.trendLinks(
|
||||
domain: viewModel.authenticationBox.domain,
|
||||
query: Mastodon.API.Trends.StatusQuery(
|
||||
offset: offset,
|
||||
|
@ -55,7 +55,7 @@ final class DiscoveryNewsViewModel {
|
||||
extension DiscoveryNewsViewModel {
|
||||
func checkServerEndpoint() async {
|
||||
do {
|
||||
_ = try await context.apiService.trendLinks(
|
||||
_ = try await APIService.shared.trendLinks(
|
||||
domain: authenticationBox.domain,
|
||||
query: .init(offset: nil, limit: nil),
|
||||
authenticationBox: authenticationBox
|
||||
|
@ -122,7 +122,7 @@ extension DiscoveryPostsViewModel.State {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.trendStatuses(
|
||||
let response = try await APIService.shared.trendStatuses(
|
||||
domain: viewModel.authenticationBox.domain,
|
||||
query: Mastodon.API.Trends.StatusQuery(
|
||||
offset: offset,
|
||||
|
@ -55,7 +55,7 @@ final class DiscoveryPostsViewModel {
|
||||
extension DiscoveryPostsViewModel {
|
||||
func checkServerEndpoint() async {
|
||||
do {
|
||||
_ = try await context.apiService.trendStatuses(
|
||||
_ = try await APIService.shared.trendStatuses(
|
||||
domain: authenticationBox.domain,
|
||||
query: .init(offset: nil, limit: nil),
|
||||
authenticationBox: authenticationBox
|
||||
|
@ -9,6 +9,7 @@ import Foundation
|
||||
import GameplayKit
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
import MastodonCore
|
||||
|
||||
extension HashtagTimelineViewModel {
|
||||
class State: GKState {
|
||||
@ -126,7 +127,7 @@ extension HashtagTimelineViewModel.State {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.hashtagTimeline(
|
||||
let response = try await APIService.shared.hashtagTimeline(
|
||||
maxID: maxID,
|
||||
hashtag: viewModel.hashtag,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
|
@ -68,7 +68,7 @@ extension HashtagTimelineViewModel {
|
||||
func followTag() {
|
||||
self.hashtagDetails.send(hashtagDetails.value?.copy(following: true))
|
||||
Task { @MainActor in
|
||||
let tag = try? await context.apiService.followTag(
|
||||
let tag = try? await APIService.shared.followTag(
|
||||
for: hashtag,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
@ -79,7 +79,7 @@ extension HashtagTimelineViewModel {
|
||||
func unfollowTag() {
|
||||
self.hashtagDetails.send(hashtagDetails.value?.copy(following: false))
|
||||
Task { @MainActor in
|
||||
let tag = try? await context.apiService.unfollowTag(
|
||||
let tag = try? await APIService.shared.unfollowTag(
|
||||
for: hashtag,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
@ -91,7 +91,7 @@ extension HashtagTimelineViewModel {
|
||||
private extension HashtagTimelineViewModel {
|
||||
func updateTagInformation() {
|
||||
Task { @MainActor in
|
||||
let tag = try? await context.apiService.getTagInformation(
|
||||
let tag = try? await APIService.shared.getTagInformation(
|
||||
for: hashtag,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
|
@ -308,14 +308,14 @@ extension HomeTimelineViewController {
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
context.publisherService.statusPublishResult.receive(on: DispatchQueue.main).sink { result in
|
||||
PublisherService.shared.statusPublishResult.receive(on: DispatchQueue.main).sink { result in
|
||||
if case .success(.edit(let status)) = result {
|
||||
self.viewModel?.hasPendingStatusEditReload = true
|
||||
self.viewModel?.dataController.update(status: .fromEntity(status.value), intent: .edit)
|
||||
}
|
||||
}.store(in: &disposeBag)
|
||||
|
||||
context.publisherService.$currentPublishProgress
|
||||
PublisherService.shared.$currentPublishProgress
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] progress in
|
||||
guard let self = self else { return }
|
||||
@ -434,7 +434,7 @@ extension HomeTimelineViewController {
|
||||
})
|
||||
.store(in: &disposeBag)
|
||||
|
||||
context.publisherService.statusPublishResult.prepend(.failure(AppError.badRequest))
|
||||
PublisherService.shared.statusPublishResult.prepend(.failure(AppError.badRequest))
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] publishResult in
|
||||
guard let self else { return }
|
||||
@ -625,7 +625,7 @@ extension HomeTimelineViewController {
|
||||
}
|
||||
|
||||
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||
guard let setting = context.settingService.currentSetting.value else { return }
|
||||
guard let setting = SettingService.shared.currentSetting.value else { return }
|
||||
|
||||
_ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none)
|
||||
}
|
||||
@ -638,7 +638,6 @@ extension HomeTimelineViewController {
|
||||
}
|
||||
|
||||
@objc func signOutAction(_ sender: UIAction) {
|
||||
guard let authContext = viewModel?.authenticationBox else { return }
|
||||
|
||||
Task { @MainActor in
|
||||
try await AuthenticationServiceProvider.shared.signOutMastodonUser(authentication: authenticationBox.authentication)
|
||||
|
@ -8,6 +8,7 @@
|
||||
import UIKit
|
||||
import MastodonUI
|
||||
import MastodonSDK
|
||||
import MastodonCore
|
||||
|
||||
extension HomeTimelineViewModel {
|
||||
|
||||
@ -25,7 +26,7 @@ extension HomeTimelineViewModel {
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: timelineMiddleLoaderTableViewCellDelegate,
|
||||
filterContext: .home,
|
||||
activeFilters: context.statusFilterService.$activeFilters
|
||||
activeFilters: StatusFilterService.shared.$activeFilters
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -11,7 +11,7 @@ extension HomeTimelineViewModel {
|
||||
let userAuthentication = authenticationBox
|
||||
.authentication
|
||||
guard let accountCreatedAt = userAuthentication.accountCreatedAt else {
|
||||
let updated = try? await context.apiService.accountVerifyCredentials(domain: userAuthentication.domain, authorization: authenticationBox.userAuthorization)
|
||||
let updated = try? await APIService.shared.accountVerifyCredentials(domain: userAuthentication.domain, authorization: authenticationBox.userAuthorization)
|
||||
guard let accountCreatedAt = updated?.createdAt else { return }
|
||||
AuthenticationServiceProvider.shared.updateAccountCreatedAt(accountCreatedAt, forAuthentication: userAuthentication)
|
||||
return
|
||||
@ -31,7 +31,7 @@ extension HomeTimelineViewModel {
|
||||
guard let self else { return }
|
||||
|
||||
do {
|
||||
let campaign = try await self.context.apiService
|
||||
let campaign = try await APIService.shared
|
||||
.getDonationCampaign(seed: seed, source: nil).value
|
||||
guard !Mastodon.Entity.DonationCampaign.hasPreviouslyDismissed(campaign.id) && !Mastodon.Entity.DonationCampaign.hasPreviouslyContributed(campaign.id) else { return }
|
||||
onPresentDonationCampaign.send(campaign)
|
||||
|
@ -115,7 +115,7 @@ extension HomeTimelineViewModel.LoadLatestState {
|
||||
}
|
||||
|
||||
do {
|
||||
await AuthenticationServiceProvider.shared.fetchAccounts(apiService: viewModel.context.apiService)
|
||||
await AuthenticationServiceProvider.shared.fetchAccounts()
|
||||
let response: Mastodon.Response.Content<[Mastodon.Entity.Status]>
|
||||
|
||||
/// To find out wether or not we need to show the "Load More" button
|
||||
@ -124,23 +124,23 @@ extension HomeTimelineViewModel.LoadLatestState {
|
||||
|
||||
switch viewModel.timelineContext {
|
||||
case .home:
|
||||
response = try await viewModel.context.apiService.homeTimeline(
|
||||
response = try await APIService.shared.homeTimeline(
|
||||
sinceID: sinceID,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
case .public:
|
||||
response = try await viewModel.context.apiService.publicTimeline(
|
||||
response = try await APIService.shared.publicTimeline(
|
||||
query: .init(local: true, sinceID: sinceID),
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
case let .list(id):
|
||||
response = try await viewModel.context.apiService.listTimeline(
|
||||
response = try await APIService.shared.listTimeline(
|
||||
id: id,
|
||||
query: .init(sinceID: sinceID),
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
case let .hashtag(tag):
|
||||
response = try await viewModel.context.apiService.hashtagTimeline(
|
||||
response = try await APIService.shared.hashtagTimeline(
|
||||
hashtag: tag,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
|
@ -59,29 +59,29 @@ extension HomeTimelineViewModel.LoadOldestState {
|
||||
}
|
||||
|
||||
do {
|
||||
await AuthenticationServiceProvider.shared.fetchAccounts(apiService: viewModel.context.apiService)
|
||||
await AuthenticationServiceProvider.shared.fetchAccounts()
|
||||
|
||||
let response: Mastodon.Response.Content<[Mastodon.Entity.Status]>
|
||||
|
||||
switch viewModel.timelineContext {
|
||||
case .home:
|
||||
response = try await viewModel.context.apiService.homeTimeline(
|
||||
response = try await APIService.shared.homeTimeline(
|
||||
maxID: maxID,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
case .public:
|
||||
response = try await viewModel.context.apiService.publicTimeline(
|
||||
response = try await APIService.shared.publicTimeline(
|
||||
query: .init(local: true, maxID: maxID),
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
case let .list(id):
|
||||
response = try await viewModel.context.apiService.listTimeline(
|
||||
id: id,
|
||||
response = try await APIService.shared.listTimeline(
|
||||
id: id,
|
||||
query: .init(local: true, maxID: maxID),
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
case let .hashtag(tag):
|
||||
response = try await viewModel.context.apiService.hashtagTimeline(
|
||||
response = try await APIService.shared.hashtagTimeline(
|
||||
hashtag: tag,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
|
@ -163,30 +163,30 @@ extension HomeTimelineViewModel {
|
||||
guard let status = record.status else { return }
|
||||
record.isLoadingMore = true
|
||||
|
||||
await AuthenticationServiceProvider.shared.fetchAccounts(apiService: context.apiService)
|
||||
await AuthenticationServiceProvider.shared.fetchAccounts()
|
||||
|
||||
// fetch data
|
||||
let response: Mastodon.Response.Content<[Mastodon.Entity.Status]>?
|
||||
|
||||
switch timelineContext {
|
||||
case .home:
|
||||
response = try? await context.apiService.homeTimeline(
|
||||
response = try? await APIService.shared.homeTimeline(
|
||||
maxID: status.id,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
case .public:
|
||||
response = try? await context.apiService.publicTimeline(
|
||||
response = try? await APIService.shared.publicTimeline(
|
||||
query: .init(local: true, maxID: status.id),
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
case let .list(id):
|
||||
response = try? await context.apiService.listTimeline(
|
||||
id: id,
|
||||
response = try? await APIService.shared.listTimeline(
|
||||
id: id,
|
||||
query: .init(local: true, maxID: status.id),
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
case let .hashtag(tag):
|
||||
response = try? await context.apiService.hashtagTimeline(
|
||||
response = try? await APIService.shared.hashtagTimeline(
|
||||
hashtag: tag,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
|
@ -274,7 +274,7 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
|
||||
switch action {
|
||||
case .savePhoto:
|
||||
guard let assetURL = viewController.viewModel.item.assetURL else { return }
|
||||
context.photoLibraryService.save(imageSource: .url(assetURL))
|
||||
PhotoLibraryService.shared.save(imageSource: .url(assetURL))
|
||||
.sink { [weak self] completion in
|
||||
guard let self = self else { return }
|
||||
switch completion {
|
||||
@ -300,7 +300,7 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
|
||||
case .copyPhoto:
|
||||
guard let assetURL = viewController.viewModel.item.assetURL else { return }
|
||||
|
||||
context.photoLibraryService.copy(imageSource: .url(assetURL))
|
||||
PhotoLibraryService.shared.copy(imageSource: .url(assetURL))
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(_):
|
||||
|
@ -149,7 +149,7 @@ class NotificationPolicyViewController: UIViewController {
|
||||
guard let self else { return }
|
||||
|
||||
do {
|
||||
let updatedPolicy = try await viewModel.appContext.apiService.updateNotificationPolicy(
|
||||
let updatedPolicy = try await APIService.shared.updateNotificationPolicy(
|
||||
authenticationBox: authenticationBox,
|
||||
filterNotFollowing: viewModel.notFollowing,
|
||||
filterNotFollowers: viewModel.noFollower,
|
||||
|
@ -136,10 +136,10 @@ extension NotificationRequestsTableViewController: NotificationRequestTableViewC
|
||||
}
|
||||
|
||||
private func acceptNotificationRequest(_ notificationRequest: MastodonSDK.Mastodon.Entity.NotificationRequest) async throws {
|
||||
_ = try await context.apiService.acceptNotificationRequests(authenticationBox: authenticationBox,
|
||||
_ = try await APIService.shared.acceptNotificationRequests(authenticationBox: authenticationBox,
|
||||
id: notificationRequest.id)
|
||||
|
||||
let requests = try await context.apiService.notificationRequests(authenticationBox: authenticationBox).value
|
||||
let requests = try await APIService.shared.notificationRequests(authenticationBox: authenticationBox).value
|
||||
|
||||
NotificationCenter.default.post(name: .notificationFilteringChanged, object: nil)
|
||||
|
||||
@ -183,10 +183,10 @@ extension NotificationRequestsTableViewController: NotificationRequestTableViewC
|
||||
}
|
||||
|
||||
private func rejectNotificationRequest(_ notificationRequest: MastodonSDK.Mastodon.Entity.NotificationRequest) async throws {
|
||||
_ = try await context.apiService.rejectNotificationRequests(authenticationBox: authenticationBox,
|
||||
_ = try await APIService.shared.rejectNotificationRequests(authenticationBox: authenticationBox,
|
||||
id: notificationRequest.id)
|
||||
|
||||
let requests = try await context.apiService.notificationRequests(authenticationBox: authenticationBox).value
|
||||
let requests = try await APIService.shared.notificationRequests(authenticationBox: authenticationBox).value
|
||||
|
||||
NotificationCenter.default.post(name: .notificationFilteringChanged, object: nil)
|
||||
|
||||
|
@ -126,7 +126,7 @@ extension NotificationTimelineViewController {
|
||||
|
||||
@objc private func refreshControlValueChanged(_ sender: RefreshControl) {
|
||||
Task {
|
||||
let policy = try? await context.apiService.notificationPolicy(authenticationBox: authenticationBox)
|
||||
let policy = try? await APIService.shared.notificationPolicy(authenticationBox: authenticationBox)
|
||||
viewModel.notificationPolicy = policy?.value
|
||||
|
||||
await viewModel.loadLatest()
|
||||
|
@ -8,6 +8,7 @@
|
||||
import UIKit
|
||||
import CoreData
|
||||
import MastodonSDK
|
||||
import MastodonCore
|
||||
|
||||
extension NotificationTimelineViewModel {
|
||||
|
||||
@ -22,7 +23,7 @@ extension NotificationTimelineViewModel {
|
||||
authenticationBox: authenticationBox,
|
||||
notificationTableViewCellDelegate: notificationTableViewCellDelegate,
|
||||
filterContext: .notifications,
|
||||
activeFilters: context.statusFilterService.$activeFilters
|
||||
activeFilters: StatusFilterService.shared.$activeFilters
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -77,7 +77,7 @@ extension NotificationTimelineViewModel.LoadOldestState {
|
||||
}
|
||||
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.notifications(
|
||||
let response = try await APIService.shared.notifications(
|
||||
maxID: maxID,
|
||||
accountID: accountID,
|
||||
scope: scope,
|
||||
|
@ -99,7 +99,7 @@ final class NotificationTimelineViewModel {
|
||||
Task { [weak self] in
|
||||
guard let self else { return }
|
||||
|
||||
let policy = try await self.context.apiService.notificationPolicy(authenticationBox: self.authenticationBox)
|
||||
let policy = try await APIService.shared.notificationPolicy(authenticationBox: self.authenticationBox)
|
||||
self.notificationPolicy = policy.value
|
||||
|
||||
await self.loadLatest()
|
||||
|
@ -100,14 +100,14 @@ extension NotificationViewController {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
// reset notification count
|
||||
context.notificationService.clearNotificationCountForActiveUser()
|
||||
NotificationService.shared.clearNotificationCountForActiveUser()
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
// reset notification count
|
||||
context.notificationService.clearNotificationCountForActiveUser()
|
||||
NotificationService.shared.clearNotificationCountForActiveUser()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
|
@ -56,7 +56,7 @@ final class NotificationViewModel {
|
||||
// end init
|
||||
Task {
|
||||
do {
|
||||
let policy = try await context.apiService.notificationPolicy(authenticationBox: authenticationBox)
|
||||
let policy = try await APIService.shared.notificationPolicy(authenticationBox: authenticationBox)
|
||||
self.notificationPolicy = policy.value
|
||||
} catch {
|
||||
// we won't show the filtering-options.
|
||||
|
@ -109,7 +109,7 @@ extension MastodonConfirmEmailViewController {
|
||||
// upload avatar and set display name in the background
|
||||
Just(self.viewModel.userToken.accessToken)
|
||||
.asyncMap { token in
|
||||
try await self.context.apiService.accountUpdateCredentials(
|
||||
try await APIService.shared.accountUpdateCredentials(
|
||||
domain: self.viewModel.authenticateInfo.domain,
|
||||
query: self.viewModel.updateCredentialQuery,
|
||||
authorization: Mastodon.API.OAuth.Authorization(accessToken: token)
|
||||
|
@ -137,7 +137,7 @@ class MastodonLoginViewController: UIViewController, NeedsDependency {
|
||||
.store(in: &disposeBag)
|
||||
|
||||
authenticationViewModel.isAuthenticating.send(true)
|
||||
context.apiService.createApplication(domain: server.domain)
|
||||
APIService.shared.createApplication(domain: server.domain)
|
||||
.tryMap { response -> AuthenticationViewModel.AuthenticateInfo in
|
||||
let application = response.value
|
||||
guard let info = AuthenticationViewModel.AuthenticateInfo(
|
||||
|
@ -28,7 +28,7 @@ class MastodonLoginViewModel {
|
||||
}
|
||||
|
||||
func updateServers() {
|
||||
appContext?.apiService.servers(registrations: "all").sink(receiveCompletion: { [weak self] completion in
|
||||
APIService.shared.servers(registrations: "all").sink(receiveCompletion: { [weak self] completion in
|
||||
switch completion {
|
||||
case .finished:
|
||||
guard let self = self else { return }
|
||||
|
@ -271,13 +271,13 @@ extension MastodonPickServerViewController {
|
||||
|
||||
authenticationViewModel.isAuthenticating.send(true)
|
||||
|
||||
context.apiService.instance(domain: server.domain, authenticationBox: nil)
|
||||
APIService.shared.instance(domain: server.domain, authenticationBox: nil)
|
||||
.compactMap { [weak self] response -> AnyPublisher<MastodonPickServerViewModel.SignUpResponseFirst, Error>? in
|
||||
guard let self = self else { return nil }
|
||||
guard response.value.registrations != false else {
|
||||
return Fail(error: AuthenticationViewModel.AuthenticationError.registrationClosed).eraseToAnyPublisher()
|
||||
}
|
||||
return self.context.apiService.createApplication(domain: server.domain)
|
||||
return APIService.shared.createApplication(domain: server.domain)
|
||||
.map { MastodonPickServerViewModel.SignUpResponseFirst(instance: response, application: $0) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
@ -299,7 +299,7 @@ extension MastodonPickServerViewController {
|
||||
guard let self = self else { return nil }
|
||||
let instance = response.instance
|
||||
let authenticateInfo = response.authenticateInfo
|
||||
return self.context.apiService.applicationAccessToken(
|
||||
return APIService.shared.applicationAccessToken(
|
||||
domain: server.domain,
|
||||
clientID: authenticateInfo.clientID,
|
||||
clientSecret: authenticateInfo.clientSecret,
|
||||
|
@ -8,6 +8,7 @@
|
||||
import Foundation
|
||||
import GameplayKit
|
||||
import MastodonSDK
|
||||
import MastodonCore
|
||||
|
||||
extension MastodonPickServerViewModel {
|
||||
class LoadIndexedServerState: GKState {
|
||||
@ -37,7 +38,7 @@ extension MastodonPickServerViewModel.LoadIndexedServerState {
|
||||
|
||||
guard let viewModel = self.viewModel, let stateMachine = self.stateMachine else { return }
|
||||
viewModel.isLoadingIndexedServers.value = true
|
||||
viewModel.context.apiService.servers(language: nil, category: nil)
|
||||
APIService.shared.servers(language: nil, category: nil)
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
|
@ -85,7 +85,7 @@ extension MastodonPickServerViewModel {
|
||||
|
||||
private func configure() {
|
||||
|
||||
context.apiService.languages().sink { completion in
|
||||
APIService.shared.languages().sink { completion in
|
||||
|
||||
} receiveValue: { response in
|
||||
self.allLanguages.value = response.value
|
||||
@ -165,9 +165,9 @@ extension MastodonPickServerViewModel {
|
||||
return Just(Result.failure(APIService.APIError.implicit(.badRequest))).eraseToAnyPublisher()
|
||||
}
|
||||
self.unindexedServers.value = nil
|
||||
return self.context.apiService.webFinger(domain: domain)
|
||||
return APIService.shared.webFinger(domain: domain)
|
||||
.flatMap { domain -> AnyPublisher<Result<Mastodon.Response.Content<[Mastodon.Entity.Server]>, Error>, Never> in
|
||||
return self.context.apiService.instance(domain: domain, authenticationBox: nil)
|
||||
return APIService.shared.instance(domain: domain, authenticationBox: nil)
|
||||
.map { response -> Result<Mastodon.Response.Content<[Mastodon.Entity.Server]>, Error>in
|
||||
let newResponse = response.map { [Mastodon.Entity.Server(domain: domain, instance: $0)] }
|
||||
return Result.success(newResponse)
|
||||
|
@ -197,7 +197,7 @@ extension MastodonRegisterViewController {
|
||||
var retryCount = 0
|
||||
|
||||
// register without show server rules
|
||||
context.apiService.accountRegister(
|
||||
APIService.shared.accountRegister(
|
||||
domain: viewModel.domain,
|
||||
query: query,
|
||||
authorization: viewModel.applicationAuthorization
|
||||
@ -222,7 +222,7 @@ extension MastodonRegisterViewController {
|
||||
locale: self.viewModel.instance.languages?.first ?? "en"
|
||||
)
|
||||
retryCount += 1
|
||||
return self.context.apiService.accountRegister(
|
||||
return APIService.shared.accountRegister(
|
||||
domain: self.viewModel.domain,
|
||||
query: retryQuery,
|
||||
authorization: self.viewModel.applicationAuthorization
|
||||
|
@ -110,7 +110,7 @@ final class MastodonRegisterViewModel: ObservableObject {
|
||||
.compactMap { [weak self] text -> AnyPublisher<Result<Mastodon.Response.Content<Mastodon.Entity.Account>, Error>, Never>? in
|
||||
guard let self = self else { return nil }
|
||||
let query = Mastodon.API.Account.AccountLookupQuery(acct: text)
|
||||
return context.apiService.accountLookup(domain: domain, query: query, authorization: self.applicationAuthorization)
|
||||
return APIService.shared.accountLookup(domain: domain, query: query, authorization: self.applicationAuthorization)
|
||||
.map {
|
||||
response -> Result<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> in
|
||||
Result.success(response)
|
||||
|
@ -22,9 +22,9 @@ extension MastodonRegisterViewController {
|
||||
viewController.context = context
|
||||
viewController.coordinator = coordinator
|
||||
|
||||
let instanceResponse = try await context.apiService.instance(domain: domain, authenticationBox: nil).singleOutput()
|
||||
let applicationResponse = try await context.apiService.createApplication(domain: domain).singleOutput()
|
||||
let accessTokenResponse = try await context.apiService.applicationAccessToken(
|
||||
let instanceResponse = try await APIService.shared.instance(domain: domain, authenticationBox: nil).singleOutput()
|
||||
let applicationResponse = try await APIService.shared.createApplication(domain: domain).singleOutput()
|
||||
let accessTokenResponse = try await APIService.shared.applicationAccessToken(
|
||||
domain: domain,
|
||||
clientID: applicationResponse.value.clientID!,
|
||||
clientSecret: applicationResponse.value.clientSecret!,
|
||||
|
@ -143,7 +143,7 @@ extension AuthenticationViewModel {
|
||||
})
|
||||
.compactMap { [weak self] code -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error>? in
|
||||
guard let self = self else { return nil }
|
||||
return self.context.apiService
|
||||
return APIService.shared
|
||||
.userAccessToken(
|
||||
domain: info.domain,
|
||||
clientID: info.clientID,
|
||||
@ -188,7 +188,7 @@ extension AuthenticationViewModel {
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||
let authorization = Mastodon.API.OAuth.Authorization(accessToken: userToken.accessToken)
|
||||
|
||||
return context.apiService.accountVerifyCredentials(
|
||||
return APIService.shared.accountVerifyCredentials(
|
||||
domain: info.domain,
|
||||
authorization: authorization
|
||||
)
|
||||
@ -224,7 +224,7 @@ extension AuthenticationViewModel {
|
||||
) async throws -> Mastodon.Entity.Account {
|
||||
let authorization = Mastodon.API.OAuth.Authorization(accessToken: userToken)
|
||||
|
||||
let account = try await context.apiService.accountVerifyCredentials(
|
||||
let account = try await APIService.shared.accountVerifyCredentials(
|
||||
domain: domain,
|
||||
authorization: authorization
|
||||
)
|
||||
|
@ -283,13 +283,13 @@ extension WelcomeViewController {
|
||||
|
||||
authenticationViewModel.isAuthenticating.send(true)
|
||||
|
||||
context.apiService.instance(domain: server.domain, authenticationBox: nil)
|
||||
APIService.shared.instance(domain: server.domain, authenticationBox: nil)
|
||||
.compactMap { [weak self] response -> AnyPublisher<MastodonPickServerViewModel.SignUpResponseFirst, Error>? in
|
||||
guard let self = self else { return nil }
|
||||
guard response.value.registrations != false else {
|
||||
return Fail(error: AuthenticationViewModel.AuthenticationError.registrationClosed).eraseToAnyPublisher()
|
||||
}
|
||||
return self.context.apiService.createApplication(domain: server.domain)
|
||||
return APIService.shared.createApplication(domain: server.domain)
|
||||
.map { MastodonPickServerViewModel.SignUpResponseFirst(instance: response, application: $0) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
@ -311,7 +311,7 @@ extension WelcomeViewController {
|
||||
guard let self = self else { return nil }
|
||||
let instance = response.instance
|
||||
let authenticateInfo = response.authenticateInfo
|
||||
return self.context.apiService.applicationAccessToken(
|
||||
return APIService.shared.applicationAccessToken(
|
||||
domain: server.domain,
|
||||
clientID: authenticateInfo.clientID,
|
||||
clientSecret: authenticateInfo.clientSecret,
|
||||
|
@ -31,7 +31,7 @@ final class WelcomeViewModel {
|
||||
}
|
||||
|
||||
func downloadDefaultServer(completion: (() -> Void)? = nil) {
|
||||
context.apiService.defaultServers()
|
||||
APIService.shared.defaultServers()
|
||||
.timeout(.milliseconds(500) , scheduler: DispatchQueue.main)
|
||||
.sink { [weak self] result in
|
||||
|
||||
|
@ -124,7 +124,7 @@ extension BookmarkViewModel.State {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.bookmarkedStatuses(
|
||||
let response = try await APIService.shared.bookmarkedStatuses(
|
||||
maxID: maxID,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
|
@ -123,7 +123,7 @@ extension FavoriteViewModel.State {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.favoritedStatuses(
|
||||
let response = try await APIService.shared.favoritedStatuses(
|
||||
maxID: maxID,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
|
@ -38,7 +38,7 @@ extension FollowedTagsViewModel {
|
||||
func fetchFollowedTags(completion: (() -> Void)? = nil ) {
|
||||
Task { @MainActor in
|
||||
do {
|
||||
followedTags = try await context.apiService.getFollowedTags(
|
||||
followedTags = try await APIService.shared.getFollowedTags(
|
||||
domain: authenticationBox.domain,
|
||||
query: Mastodon.API.Account.FollowedTagsQuery(limit: nil),
|
||||
authenticationBox: authenticationBox
|
||||
@ -59,12 +59,12 @@ extension FollowedTagsViewModel {
|
||||
func followOrUnfollow(_ tag: Mastodon.Entity.Tag) {
|
||||
Task { @MainActor in
|
||||
if tag.following ?? false {
|
||||
_ = try? await context.apiService.unfollowTag(
|
||||
_ = try? await APIService.shared.unfollowTag(
|
||||
for: tag.name,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
} else {
|
||||
_ = try? await context.apiService.followTag(
|
||||
_ = try? await APIService.shared.followTag(
|
||||
for: tag.name,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
|
@ -138,7 +138,7 @@ extension FollowerListViewModel.State {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let accountResponse = try await viewModel.context.apiService.followers(
|
||||
let accountResponse = try await APIService.shared.followers(
|
||||
userID: userID,
|
||||
maxID: maxID,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
@ -154,7 +154,7 @@ extension FollowerListViewModel.State {
|
||||
|
||||
var hasNewAppend = false
|
||||
|
||||
let newRelationships = try await viewModel.context.apiService.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox)
|
||||
let newRelationships = try await APIService.shared.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox)
|
||||
|
||||
var accounts = viewModel.accounts
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
import Foundation
|
||||
import GameplayKit
|
||||
import MastodonSDK
|
||||
import MastodonCore
|
||||
|
||||
extension FollowingListViewModel {
|
||||
class State: GKState {
|
||||
@ -133,7 +134,7 @@ extension FollowingListViewModel.State {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let accountResponse = try await viewModel.context.apiService.following(
|
||||
let accountResponse = try await APIService.shared.following(
|
||||
userID: userID,
|
||||
maxID: maxID,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
@ -149,7 +150,7 @@ extension FollowingListViewModel.State {
|
||||
|
||||
var hasNewAppend = false
|
||||
|
||||
let newRelationships = try await viewModel.context.apiService.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox)
|
||||
let newRelationships = try await APIService.shared.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox)
|
||||
|
||||
var accounts = viewModel.accounts
|
||||
|
||||
|
@ -405,7 +405,7 @@ extension ProfileViewController {
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
context.publisherService.statusPublishResult.sink { [weak self] result in
|
||||
PublisherService.shared.statusPublishResult.sink { [weak self] result in
|
||||
if case .success(.edit(let status)) = result {
|
||||
self?.updateViewModelsWithDataControllers(status: .fromEntity(status.value), intent: .edit)
|
||||
}
|
||||
@ -576,7 +576,7 @@ extension ProfileViewController {
|
||||
}
|
||||
|
||||
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||
guard let setting = context.settingService.currentSetting.value else { return }
|
||||
guard let setting = SettingService.shared.currentSetting.value else { return }
|
||||
|
||||
_ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none)
|
||||
}
|
||||
@ -645,15 +645,15 @@ extension ProfileViewController {
|
||||
|
||||
let account = viewModel.account
|
||||
if let domain = account.domain,
|
||||
let updatedAccount = try? await context.apiService.fetchUser(username: account.acct, domain: domain, authenticationBox: viewModel.authenticationBox),
|
||||
let updatedRelationship = try? await context.apiService.relationship(forAccounts: [updatedAccount], authenticationBox: viewModel.authenticationBox).value.first
|
||||
let updatedAccount = try? await APIService.shared.fetchUser(username: account.acct, domain: domain, authenticationBox: viewModel.authenticationBox),
|
||||
let updatedRelationship = try? await APIService.shared.relationship(forAccounts: [updatedAccount], authenticationBox: viewModel.authenticationBox).value.first
|
||||
{
|
||||
viewModel.account = updatedAccount
|
||||
viewModel.relationship = updatedRelationship
|
||||
viewModel.profileAboutViewModel.fields = updatedAccount.mastodonFields
|
||||
}
|
||||
|
||||
if let updatedMe = try? await context.apiService.authenticatedUserInfo(authenticationBox: viewModel.authenticationBox).value {
|
||||
if let updatedMe = try? await APIService.shared.authenticatedUserInfo(authenticationBox: viewModel.authenticationBox).value {
|
||||
viewModel.me = updatedMe
|
||||
FileManager.default.store(account: updatedMe, forUserID: viewModel.authenticationBox.authentication.userIdentifier())
|
||||
}
|
||||
@ -916,7 +916,7 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
|
||||
Task {
|
||||
_ = try await DataSourceFacade.responseToDomainBlockAction(dependency: self, account: account)
|
||||
|
||||
guard let newRelationship = try await self.context.apiService.relationship(forAccounts: [account], authenticationBox: viewModel.authenticationBox).value.first else { return }
|
||||
guard let newRelationship = try await APIService.shared.relationship(forAccounts: [account], authenticationBox: viewModel.authenticationBox).value.first else { return }
|
||||
|
||||
viewModel.isUpdating = false
|
||||
|
||||
@ -1084,7 +1084,7 @@ extension ProfileViewController {
|
||||
Task {
|
||||
let account = viewModel.account
|
||||
if let domain = account.domain,
|
||||
let updatedAccount = try? await context.apiService.fetchUser(username: account.acct, domain: domain, authenticationBox: viewModel.authenticationBox) {
|
||||
let updatedAccount = try? await APIService.shared.fetchUser(username: account.acct, domain: domain, authenticationBox: viewModel.authenticationBox) {
|
||||
viewModel.account = updatedAccount
|
||||
|
||||
viewModel.relationship = relationship
|
||||
@ -1097,7 +1097,7 @@ extension ProfileViewController {
|
||||
} else if viewModel.account == viewModel.me {
|
||||
// update my profile
|
||||
Task {
|
||||
if let updatedMe = try? await context.apiService.authenticatedUserInfo(authenticationBox: viewModel.authenticationBox).value {
|
||||
if let updatedMe = try? await APIService.shared.authenticatedUserInfo(authenticationBox: viewModel.authenticationBox).value {
|
||||
viewModel.me = updatedMe
|
||||
viewModel.account = updatedMe
|
||||
FileManager.default.store(account: updatedMe, forUserID: viewModel.authenticationBox.authentication.userIdentifier())
|
||||
|
@ -134,7 +134,7 @@ class ProfileViewModel: NSObject {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let response = try await self.context.apiService.relationship(
|
||||
let response = try await APIService.shared.relationship(
|
||||
forAccounts: [account],
|
||||
authenticationBox: self.authenticationBox
|
||||
)
|
||||
@ -178,7 +178,7 @@ class ProfileViewModel: NSObject {
|
||||
|
||||
let mastodonAuthentication = authenticationBox.authentication
|
||||
let authorization = Mastodon.API.OAuth.Authorization(accessToken: mastodonAuthentication.userAccessToken)
|
||||
return context.apiService.accountVerifyCredentials(domain: domain, authorization: authorization)
|
||||
return APIService.shared.accountVerifyCredentials(domain: domain, authorization: authorization)
|
||||
.tryMap { response in
|
||||
FileManager.default.store(account: response.value, forUserID: mastodonAuthentication.userIdentifier())
|
||||
return response
|
||||
@ -227,7 +227,7 @@ extension ProfileViewModel {
|
||||
source: nil,
|
||||
fieldsAttributes: fieldsAttributes
|
||||
)
|
||||
let response = try await context.apiService.accountUpdateCredentials(
|
||||
let response = try await APIService.shared.accountUpdateCredentials(
|
||||
domain: domain,
|
||||
query: query,
|
||||
authorization: authorization
|
||||
|
@ -126,7 +126,7 @@ extension UserTimelineViewModel.State {
|
||||
let queryFilter = viewModel.queryFilter
|
||||
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.userTimeline(
|
||||
let response = try await APIService.shared.userTimeline(
|
||||
accountID: userID,
|
||||
maxID: maxID,
|
||||
sinceID: nil,
|
||||
|
@ -8,6 +8,7 @@
|
||||
import Foundation
|
||||
import GameplayKit
|
||||
import MastodonSDK
|
||||
import MastodonCore
|
||||
|
||||
extension UserListViewModel {
|
||||
class State: GKState {
|
||||
@ -128,13 +129,13 @@ extension UserListViewModel.State {
|
||||
let accountResponse: Mastodon.Response.Content<[Mastodon.Entity.Account]>
|
||||
switch viewModel.kind {
|
||||
case .favoritedBy(let status):
|
||||
accountResponse = try await viewModel.context.apiService.favoritedBy(
|
||||
accountResponse = try await APIService.shared.favoritedBy(
|
||||
status: status,
|
||||
query: .init(maxID: maxID, limit: nil),
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
case .rebloggedBy(let status):
|
||||
accountResponse = try await viewModel.context.apiService.rebloggedBy(
|
||||
accountResponse = try await APIService.shared.rebloggedBy(
|
||||
status: status,
|
||||
query: .init(maxID: maxID, limit: nil),
|
||||
authenticationBox: authenticationBox
|
||||
@ -151,7 +152,7 @@ extension UserListViewModel.State {
|
||||
|
||||
var hasNewAppend = false
|
||||
|
||||
let newRelationships = try await viewModel.context.apiService.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox)
|
||||
let newRelationships = try await APIService.shared.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox)
|
||||
|
||||
var accounts = viewModel.accounts
|
||||
|
||||
|
@ -67,7 +67,7 @@ class ReportViewModel {
|
||||
// bind server rules
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let response = try await context.apiService.instance(domain: authenticationBox.domain, authenticationBox: authenticationBox)
|
||||
let response = try await APIService.shared.instance(domain: authenticationBox.domain, authenticationBox: authenticationBox)
|
||||
.timeout(3, scheduler: DispatchQueue.main)
|
||||
.singleOutput()
|
||||
let rules = response.value.rules ?? []
|
||||
@ -143,7 +143,7 @@ extension ReportViewModel {
|
||||
|
||||
do {
|
||||
isReporting = true
|
||||
let _ = try await context.apiService.report(
|
||||
let _ = try await APIService.shared.report(
|
||||
query: query,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
|
@ -10,6 +10,7 @@ import Foundation
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import GameplayKit
|
||||
import MastodonCore
|
||||
|
||||
extension ReportStatusViewModel {
|
||||
class State: GKState {
|
||||
@ -69,7 +70,7 @@ extension ReportStatusViewModel.State {
|
||||
let maxID = await viewModel.dataController.records.last?.id
|
||||
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.userTimeline(
|
||||
let response = try await APIService.shared.userTimeline(
|
||||
accountID: viewModel.account.id,
|
||||
maxID: maxID,
|
||||
sinceID: nil,
|
||||
|
@ -136,7 +136,7 @@ extension MainTabBarController {
|
||||
}
|
||||
}
|
||||
|
||||
context.apiService.error
|
||||
APIService.shared.error
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] error in
|
||||
guard let self, let coordinator = self.coordinator else { return }
|
||||
@ -161,7 +161,7 @@ extension MainTabBarController {
|
||||
// handle push notification.
|
||||
// toggle entry when finish fetch latest notification
|
||||
Publishers.CombineLatest(
|
||||
context.notificationService.unreadNotificationCountDidUpdate,
|
||||
NotificationService.shared.unreadNotificationCountDidUpdate,
|
||||
$currentTab
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
@ -380,7 +380,7 @@ extension MainTabBarController {
|
||||
guard let authenticationBox else { return }
|
||||
|
||||
Task { @MainActor in
|
||||
let profileResponse = try await context.apiService.authenticatedUserInfo(authenticationBox: authenticationBox)
|
||||
let profileResponse = try await APIService.shared.authenticatedUserInfo(authenticationBox: authenticationBox)
|
||||
FileManager.default.store(account: profileResponse.value, forUserID: authenticationBox.authentication.userIdentifier())
|
||||
}
|
||||
}
|
||||
@ -519,7 +519,7 @@ extension MainTabBarController {
|
||||
}
|
||||
|
||||
// open settings
|
||||
if context.settingService.currentSetting.value != nil {
|
||||
if SettingService.shared.currentSetting.value != nil {
|
||||
commands.append(openSettingsKeyCommand)
|
||||
}
|
||||
}
|
||||
@ -562,7 +562,7 @@ extension MainTabBarController {
|
||||
}
|
||||
|
||||
@objc private func openSettingsKeyCommandHandler(_ sender: UIKeyCommand) {
|
||||
guard let setting = context.settingService.currentSetting.value else { return }
|
||||
guard let setting = SettingService.shared.currentSetting.value else { return }
|
||||
|
||||
_ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none)
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ extension SidebarViewController: UICollectionViewDelegate {
|
||||
case .tab(let tab):
|
||||
delegate?.sidebarViewController(self, didSelectTab: tab)
|
||||
case .setting:
|
||||
guard let setting = context.settingService.currentSetting.value else { return }
|
||||
guard let setting = SettingService.shared.currentSetting.value else { return }
|
||||
|
||||
_ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none)
|
||||
case .compose:
|
||||
|
@ -107,7 +107,7 @@ extension SidebarViewModel {
|
||||
switch item {
|
||||
case .notifications:
|
||||
Publishers.CombineLatest(
|
||||
self.context.notificationService.unreadNotificationCountDidUpdate,
|
||||
NotificationService.shared.unreadNotificationCountDidUpdate,
|
||||
self.$currentTab
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
@ -71,7 +71,7 @@ extension SearchResultOverviewCoordinator: SearchResultsOverviewTableViewControl
|
||||
let authenticationBox = self.authenticationBox
|
||||
|
||||
Task {
|
||||
let searchResult = try await context.apiService.search(
|
||||
let searchResult = try await APIService.shared.search(
|
||||
query: query,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
@ -124,7 +124,7 @@ extension SearchResultOverviewCoordinator: SearchResultsOverviewTableViewControl
|
||||
)
|
||||
|
||||
Task {
|
||||
let searchResult = try await context.apiService.search(
|
||||
let searchResult = try await APIService.shared.search(
|
||||
query: query,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
|
@ -156,7 +156,7 @@ class SearchResultsOverviewTableViewController: UIViewController, NeedsDependenc
|
||||
|
||||
let searchTask = Task {
|
||||
do {
|
||||
let searchResult = try await context.apiService.search(
|
||||
let searchResult = try await APIService.shared.search(
|
||||
query: query,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
|
@ -113,7 +113,7 @@ extension SearchResultViewModel.State {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let searchResults = try await viewModel.context.apiService.search(
|
||||
let searchResults = try await APIService.shared.search(
|
||||
query: query,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
).value
|
||||
@ -129,7 +129,7 @@ extension SearchResultViewModel.State {
|
||||
|
||||
let relationships: [Mastodon.Entity.Relationship]
|
||||
if accounts.isNotEmpty {
|
||||
relationships = try await viewModel.context.apiService.relationship(
|
||||
relationships = try await APIService.shared.relationship(
|
||||
forAccounts: accounts,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
).value
|
||||
|
@ -145,7 +145,7 @@ extension PrivacySafetyViewModel {
|
||||
let domain = authenticationBox.domain
|
||||
let userAuthorization = authenticationBox.userAuthorization
|
||||
|
||||
let account = try await appContext.apiService.accountVerifyCredentials(
|
||||
let account = try await APIService.shared.accountVerifyCredentials(
|
||||
domain: domain,
|
||||
authorization: userAuthorization
|
||||
).singleOutput().value
|
||||
@ -172,7 +172,7 @@ extension PrivacySafetyViewModel {
|
||||
let domain = authenticationBox.domain
|
||||
let userAuthorization = authenticationBox.userAuthorization
|
||||
|
||||
let _ = try await appContext.apiService.accountUpdateCredentials(
|
||||
let _ = try await APIService.shared.accountUpdateCredentials(
|
||||
domain: domain,
|
||||
query: .init(
|
||||
discoverable: suggestMyAccountToOthers,
|
||||
|
@ -48,7 +48,7 @@ class SettingsCoordinator: NSObject, Coordinator {
|
||||
let userAuthentication = s.authenticationBox.authentication
|
||||
let seed = Mastodon.Entity.DonationCampaign.donationSeed(username: userAuthentication.username, domain: userAuthentication.domain)
|
||||
do {
|
||||
let campaign = try await s.appContext.apiService.getDonationCampaign(seed: seed, source: nil).value
|
||||
let campaign = try await APIService.shared.getDonationCampaign(seed: seed, source: nil).value
|
||||
|
||||
await MainActor.run {
|
||||
s.settingsViewController.donationCampaign = campaign
|
||||
@ -84,8 +84,8 @@ extension SettingsCoordinator: SettingsViewControllerDelegate {
|
||||
navigationController.pushViewController(generalSettingsViewController, animated: true)
|
||||
case .notifications:
|
||||
|
||||
let currentSetting = appContext.settingService.currentSetting.value
|
||||
let notificationsEnabled = appContext.notificationService.isNotificationPermissionGranted.value
|
||||
let currentSetting = SettingService.shared.currentSetting.value
|
||||
let notificationsEnabled = NotificationService.shared.isNotificationPermissionGranted.value
|
||||
let notificationViewController = NotificationSettingsViewController(currentSetting: currentSetting, notificationsEnabled: notificationsEnabled)
|
||||
notificationViewController.delegate = self
|
||||
|
||||
@ -101,7 +101,7 @@ extension SettingsCoordinator: SettingsViewControllerDelegate {
|
||||
let serverDetailsViewController = ServerDetailsViewController(domain: domain, appContext: appContext, authenticationBox: authenticationBox, sceneCoordinator: sceneCoordinator)
|
||||
serverDetailsViewController.delegate = self
|
||||
|
||||
appContext.apiService.instanceV2(domain: domain, authenticationBox: authenticationBox)
|
||||
APIService.shared.instanceV2(domain: domain, authenticationBox: authenticationBox)
|
||||
.sink { _ in
|
||||
|
||||
} receiveValue: { content in
|
||||
@ -109,7 +109,7 @@ extension SettingsCoordinator: SettingsViewControllerDelegate {
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
appContext.apiService.extendedDescription(domain: domain, authenticationBox: authenticationBox)
|
||||
APIService.shared.extendedDescription(domain: domain, authenticationBox: authenticationBox)
|
||||
.sink { _ in
|
||||
|
||||
} receiveValue: { content in
|
||||
@ -219,7 +219,7 @@ extension SettingsCoordinator: NotificationSettingsViewControllerDelegate {
|
||||
guard let subscription = setting.activeSubscription,
|
||||
setting.domain == authenticationBox.domain,
|
||||
setting.userID == authenticationBox.userID,
|
||||
let legacyViewModel = appContext.notificationService.dequeueNotificationViewModel(mastodonAuthenticationBox: authenticationBox), let deviceToken = appContext.notificationService.deviceToken.value else { return }
|
||||
let legacyViewModel = NotificationService.shared.dequeueNotificationViewModel(mastodonAuthenticationBox: authenticationBox), let deviceToken = NotificationService.shared.deviceToken.value else { return }
|
||||
|
||||
let queryData = Mastodon.API.Subscriptions.QueryData(
|
||||
policy: viewModel.selectedPolicy.subscriptionPolicy,
|
||||
@ -237,7 +237,7 @@ extension SettingsCoordinator: NotificationSettingsViewControllerDelegate {
|
||||
mastodonAuthenticationBox: authenticationBox
|
||||
)
|
||||
|
||||
appContext.apiService.createSubscription(
|
||||
APIService.shared.createSubscription(
|
||||
subscriptionObjectID: subscription.objectID,
|
||||
query: query,
|
||||
mastodonAuthenticationBox: authenticationBox
|
||||
@ -260,7 +260,7 @@ extension SettingsCoordinator: NotificationSettingsViewControllerDelegate {
|
||||
extension SettingsCoordinator: PolicySelectionViewControllerDelegate {
|
||||
func newPolicySelected(_ viewController: PolicySelectionViewController, newPolicy: NotificationPolicy) {
|
||||
self.setting.activeSubscription?.policyRaw = newPolicy.subscriptionPolicy.rawValue
|
||||
try? self.appContext.managedObjectContext.save()
|
||||
try? PersistenceManager.shared.managedObjectContext.save()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ extension UserTableViewCellDelegate where Self: ViewControllerWithDependencies &
|
||||
// Otherwise the relationship might still be `pending`
|
||||
try await Task.sleep(for: .seconds(1))
|
||||
|
||||
let relationship = try await self.context.apiService.relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first
|
||||
let relationship = try await APIService.shared.relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first
|
||||
|
||||
let isMe: Bool
|
||||
if let me {
|
||||
|
@ -50,7 +50,7 @@ final class SuggestionAccountViewModel: NSObject {
|
||||
Task {
|
||||
var suggestedAccounts: [Mastodon.Entity.V2.SuggestionAccount] = []
|
||||
do {
|
||||
let response = try await context.apiService.suggestionAccountV2(
|
||||
let response = try await APIService.shared.suggestionAccountV2(
|
||||
query: .init(limit: 5),
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
@ -60,7 +60,7 @@ final class SuggestionAccountViewModel: NSObject {
|
||||
|
||||
let accounts = suggestedAccounts.compactMap { $0.account }
|
||||
|
||||
let relationships = try await context.apiService.relationship(
|
||||
let relationships = try await APIService.shared.relationship(
|
||||
forAccounts: accounts,
|
||||
authenticationBox: authenticationBox
|
||||
).value
|
||||
|
@ -24,7 +24,7 @@ final class RemoteThreadViewModel: ThreadViewModel {
|
||||
)
|
||||
|
||||
Task { @MainActor in
|
||||
let response = try await context.apiService.status(
|
||||
let response = try await APIService.shared.status(
|
||||
statusID: statusID,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
@ -47,7 +47,7 @@ final class RemoteThreadViewModel: ThreadViewModel {
|
||||
)
|
||||
|
||||
Task { @MainActor in
|
||||
let response = try await context.apiService.notification(
|
||||
let response = try await APIService.shared.notification(
|
||||
notificationID: notificationID,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
|
@ -29,7 +29,7 @@ extension ThreadViewModel {
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: nil,
|
||||
filterContext: .thread,
|
||||
activeFilters: context.statusFilterService.$activeFilters
|
||||
activeFilters: StatusFilterService.shared.$activeFilters
|
||||
)
|
||||
)
|
||||
|
||||
@ -78,7 +78,7 @@ extension ThreadViewModel {
|
||||
newSnapshot.appendSections([.main])
|
||||
|
||||
// top loader
|
||||
let _hasReplyTo: Bool? = try? await self.context.managedObjectContext.perform {
|
||||
let _hasReplyTo: Bool? = try? await PersistenceManager.shared.managedObjectContext.perform {
|
||||
guard case let .root(threadContext) = root else { return nil }
|
||||
return threadContext.status.entity.inReplyToID != nil
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import Combine
|
||||
import GameplayKit
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
import MastodonCore
|
||||
|
||||
extension ThreadViewModel {
|
||||
class LoadThreadState: GKState {
|
||||
@ -60,7 +61,7 @@ extension ThreadViewModel.LoadThreadState {
|
||||
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.statusContext(
|
||||
let response = try await APIService.shared.statusContext(
|
||||
statusID: threadContext.statusID,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
@ -70,7 +71,7 @@ extension ThreadViewModel.LoadThreadState {
|
||||
// assert(!Thread.isMainThread)
|
||||
// await Task.sleep(1_000_000_000) // 1s delay to prevent UI render issue
|
||||
|
||||
_ = try await viewModel.context.apiService.getHistory(forStatusID: threadContext.statusID,
|
||||
_ = try await APIService.shared.getHistory(forStatusID: threadContext.statusID,
|
||||
authenticationBox: viewModel.authenticationBox)
|
||||
|
||||
viewModel.mastodonStatusThreadViewModel.appendAncestor(
|
||||
|
@ -81,7 +81,7 @@ class ThreadViewModel {
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
context.publisherService
|
||||
PublisherService.shared
|
||||
.statusPublishResult
|
||||
.sink { [weak self] value in
|
||||
guard let self else { return }
|
||||
|
@ -65,7 +65,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
extension AppDelegate {
|
||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||
appContext.notificationService.deviceToken.value = deviceToken
|
||||
NotificationService.shared.deviceToken.value = deviceToken
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,16 +85,16 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
|
||||
|
||||
let accessToken = pushNotification.accessToken
|
||||
UserDefaults.shared.increaseNotificationCount(accessToken: accessToken)
|
||||
appContext.notificationService.applicationIconBadgeNeedsUpdate.send()
|
||||
NotificationService.shared.applicationIconBadgeNeedsUpdate.send()
|
||||
|
||||
appContext.notificationService.handle(pushNotification: pushNotification)
|
||||
NotificationService.shared.handle(pushNotification: pushNotification)
|
||||
completionHandler([.sound])
|
||||
}
|
||||
|
||||
|
||||
// notification present in the background (or resume from background)
|
||||
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) async -> UIBackgroundFetchResult {
|
||||
let shortcutItems = try? await appContext.notificationService.unreadApplicationShortcutItems()
|
||||
let shortcutItems = try? await NotificationService.shared.unreadApplicationShortcutItems()
|
||||
UIApplication.shared.shortcutItems = shortcutItems
|
||||
return .noData
|
||||
}
|
||||
@ -111,8 +111,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
appContext.notificationService.handle(pushNotification: pushNotification)
|
||||
appContext.notificationService.requestRevealNotificationPublisher.send(pushNotification)
|
||||
NotificationService.shared.handle(pushNotification: pushNotification)
|
||||
NotificationService.shared.requestRevealNotificationPublisher.send(pushNotification)
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
@ -126,10 +126,3 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AppContext {
|
||||
static var shared: AppContext {
|
||||
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
||||
return appDelegate.appContext
|
||||
}
|
||||
}
|
||||
|
@ -89,10 +89,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||
|
||||
// update application badge
|
||||
AppContext.shared.notificationService.applicationIconBadgeNeedsUpdate.send()
|
||||
NotificationService.shared.applicationIconBadgeNeedsUpdate.send()
|
||||
|
||||
// trigger status filter update
|
||||
AppContext.shared.statusFilterService.filterUpdatePublisher.send()
|
||||
StatusFilterService.shared.filterUpdatePublisher.send()
|
||||
|
||||
// trigger authenticated user account update
|
||||
AuthenticationServiceProvider.shared.updateActiveUserAccountPublisher.send()
|
||||
@ -141,12 +141,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
Task {
|
||||
guard let me = authenticationBox.authentication.account() else { return }
|
||||
|
||||
guard let account = try await AppContext.shared.apiService.search(
|
||||
guard let account = try await APIService.shared.search(
|
||||
query: .init(q: incomingURL.absoluteString, type: .accounts, resolve: true),
|
||||
authenticationBox: authenticationBox
|
||||
).value.accounts.first else { return }
|
||||
|
||||
guard let relationship = try await AppContext.shared.apiService.relationship(
|
||||
guard let relationship = try await APIService.shared.relationship(
|
||||
forAccounts: [account],
|
||||
authenticationBox: authenticationBox
|
||||
).value.first else { return }
|
||||
@ -167,7 +167,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
case (profile, statusID):
|
||||
Task {
|
||||
guard let statusOnMyInstance = try await AppContext.shared.apiService.search(query: .init(q: incomingURL.absoluteString, resolve: true), authenticationBox: authenticationBox).value.statuses.first else { return }
|
||||
guard let statusOnMyInstance = try await APIService.shared.search(query: .init(q: incomingURL.absoluteString, resolve: true), authenticationBox: authenticationBox).value.statuses.first else { return }
|
||||
|
||||
let threadViewModel = RemoteThreadViewModel(
|
||||
context: AppContext.shared,
|
||||
@ -285,12 +285,12 @@ extension SceneDelegate {
|
||||
do {
|
||||
guard let me = authenticationBox.authentication.account() else { return }
|
||||
|
||||
guard let account = try await AppContext.shared.apiService.search(
|
||||
guard let account = try await APIService.shared.search(
|
||||
query: .init(q: components[1], type: .accounts, resolve: true),
|
||||
authenticationBox: authenticationBox
|
||||
).value.accounts.first else { return }
|
||||
|
||||
guard let relationship = try await AppContext.shared.apiService.relationship(
|
||||
guard let relationship = try await APIService.shared.relationship(
|
||||
forAccounts: [account],
|
||||
authenticationBox: authenticationBox
|
||||
).value.first else { return }
|
||||
|
@ -23,8 +23,7 @@ class FollowersCountIntentHandler: INExtension, FollowersCountIntentHandling {
|
||||
return INObjectCollection(items: [])
|
||||
}
|
||||
|
||||
let results = try await AppContext.shared
|
||||
.apiService
|
||||
let results = try await APIService.shared
|
||||
.search(query: .init(q: searchTerm), authenticationBox: authenticationBox)
|
||||
|
||||
return INObjectCollection(items: results.value.accounts.map { $0.acctWithDomainIfMissing(authenticationBox.domain) as NSString })
|
||||
|
@ -16,7 +16,7 @@ class HashtagIntentHandler: INExtension, HashtagIntentHandling {
|
||||
var results: [NSString] = []
|
||||
|
||||
if let searchTerm, searchTerm.isEmpty == false {
|
||||
let searchResults = try await AppContext.shared.apiService
|
||||
let searchResults = try await APIService.shared
|
||||
.search(query: .init(q: searchTerm, type: .hashtags), authenticationBox: authenticationBox)
|
||||
.value
|
||||
.hashtags
|
||||
@ -25,7 +25,7 @@ class HashtagIntentHandler: INExtension, HashtagIntentHandling {
|
||||
results = searchResults
|
||||
|
||||
} else {
|
||||
let followedTags = try await AppContext.shared.apiService.getFollowedTags(
|
||||
let followedTags = try await APIService.shared.getFollowedTags(
|
||||
domain: authenticationBox.domain,
|
||||
query: Mastodon.API.Account.FollowedTagsQuery(limit: nil),
|
||||
authenticationBox: authenticationBox)
|
||||
|
@ -15,8 +15,7 @@ class MultiFollowersCountIntentHandler: INExtension, MultiFollowersCountIntentHa
|
||||
return INObjectCollection(items: [])
|
||||
}
|
||||
|
||||
let results = try await AppContext.shared
|
||||
.apiService
|
||||
let results = try await APIService.shared
|
||||
.search(query: .init(q: searchTerm), authenticationBox: authenticationBox)
|
||||
|
||||
return INObjectCollection(items: results.value.accounts.map { $0.acctWithDomainIfMissing(authenticationBox.domain) as NSString })
|
||||
|
@ -20,10 +20,7 @@ final class SendPostIntentHandler: NSObject {
|
||||
let coreDataStack = CoreDataStack()
|
||||
lazy var managedObjectContext = coreDataStack.persistentContainer.viewContext
|
||||
lazy var api: APIService = {
|
||||
let backgroundManagedObjectContext = coreDataStack.newTaskContext()
|
||||
return APIService(
|
||||
backgroundManagedObjectContext: backgroundManagedObjectContext
|
||||
)
|
||||
return APIService.isolatedService()
|
||||
}()
|
||||
}
|
||||
|
||||
|
@ -12,26 +12,39 @@ import CoreData
|
||||
import CoreDataStack
|
||||
import AlamofireImage
|
||||
|
||||
public class AppContext: ObservableObject {
|
||||
public static let shared = AppContext()
|
||||
|
||||
public var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
public class PersistenceManager {
|
||||
public static let shared = { PersistenceManager() }()
|
||||
public let coreDataStack: CoreDataStack
|
||||
public let managedObjectContext: NSManagedObjectContext
|
||||
public let backgroundManagedObjectContext: NSManagedObjectContext
|
||||
|
||||
public let apiService: APIService
|
||||
public let emojiService: EmojiService
|
||||
private var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
private init() {
|
||||
let _coreDataStack = CoreDataStack()
|
||||
let _managedObjectContext = _coreDataStack.persistentContainer.viewContext
|
||||
let _backgroundManagedObjectContext = _coreDataStack.persistentContainer.newBackgroundContext()
|
||||
|
||||
coreDataStack = _coreDataStack
|
||||
managedObjectContext = _managedObjectContext
|
||||
backgroundManagedObjectContext = _backgroundManagedObjectContext
|
||||
|
||||
backgroundManagedObjectContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
||||
NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave, object: backgroundManagedObjectContext)
|
||||
.sink { [weak self] notification in
|
||||
guard let self = self else { return }
|
||||
self.managedObjectContext.perform {
|
||||
self.managedObjectContext.mergeChanges(fromContextDidSave: notification)
|
||||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
}
|
||||
|
||||
public let publisherService: PublisherService
|
||||
public let notificationService: NotificationService
|
||||
public let settingService: SettingService
|
||||
public let instanceService: InstanceService
|
||||
|
||||
public let blockDomainService: BlockDomainService
|
||||
public let statusFilterService: StatusFilterService
|
||||
public let photoLibraryService = PhotoLibraryService()
|
||||
public class AppContext: ObservableObject {
|
||||
public static let shared = { AppContext() }()
|
||||
|
||||
public var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
public let placeholderImageCacheService = PlaceholderImageCacheService()
|
||||
public let blurhashImageCacheService = BlurhashImageCacheService.shared
|
||||
@ -48,57 +61,6 @@ public class AppContext: ObservableObject {
|
||||
private init() {
|
||||
|
||||
let authProvider = AuthenticationServiceProvider.shared
|
||||
let _coreDataStack = CoreDataStack()
|
||||
if authProvider.authenticationMigrationRequired {
|
||||
authProvider.migrateLegacyAuthentications(
|
||||
in: _coreDataStack.persistentContainer.viewContext
|
||||
)
|
||||
}
|
||||
|
||||
let _managedObjectContext = _coreDataStack.persistentContainer.viewContext
|
||||
let _backgroundManagedObjectContext = _coreDataStack.persistentContainer.newBackgroundContext()
|
||||
|
||||
coreDataStack = _coreDataStack
|
||||
managedObjectContext = _managedObjectContext
|
||||
backgroundManagedObjectContext = _backgroundManagedObjectContext
|
||||
|
||||
let _apiService = APIService(backgroundManagedObjectContext: _backgroundManagedObjectContext)
|
||||
apiService = _apiService
|
||||
|
||||
// let _authenticationService = AuthenticationService(
|
||||
// managedObjectContext: _managedObjectContext,
|
||||
// backgroundManagedObjectContext: _backgroundManagedObjectContext,
|
||||
// apiService: _apiService
|
||||
// )
|
||||
// authenticationService = _authenticationService
|
||||
|
||||
emojiService = EmojiService(
|
||||
apiService: apiService
|
||||
)
|
||||
|
||||
publisherService = .init(apiService: _apiService)
|
||||
|
||||
let _notificationService = NotificationService(
|
||||
apiService: _apiService
|
||||
)
|
||||
notificationService = _notificationService
|
||||
|
||||
settingService = SettingService(
|
||||
apiService: _apiService,
|
||||
notificationService: _notificationService
|
||||
)
|
||||
|
||||
instanceService = InstanceService(
|
||||
apiService: _apiService
|
||||
)
|
||||
|
||||
blockDomainService = BlockDomainService(
|
||||
backgroundManagedObjectContext: _backgroundManagedObjectContext
|
||||
)
|
||||
|
||||
statusFilterService = StatusFilterService(
|
||||
apiService: _apiService
|
||||
)
|
||||
|
||||
documentStore = DocumentStore()
|
||||
documentStoreSubscription = documentStore.objectWillChange
|
||||
@ -106,16 +68,6 @@ public class AppContext: ObservableObject {
|
||||
.sink { [unowned self] in
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
backgroundManagedObjectContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
||||
NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave, object: backgroundManagedObjectContext)
|
||||
.sink { [weak self] notification in
|
||||
guard let self = self else { return }
|
||||
self.managedObjectContext.perform {
|
||||
self.managedObjectContext.mergeChanges(fromContextDidSave: notification)
|
||||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,11 @@ public class AuthenticationServiceProvider: ObservableObject {
|
||||
.assign(to: &$mastodonAuthenticationBoxes)
|
||||
|
||||
Task {
|
||||
if authenticationMigrationRequired {
|
||||
migrateLegacyAuthentications(
|
||||
in: PersistenceManager.shared.managedObjectContext
|
||||
)
|
||||
}
|
||||
await prepareForUse()
|
||||
authentications = authenticationSortedByActivation()
|
||||
}
|
||||
@ -146,7 +151,7 @@ public extension AuthenticationServiceProvider {
|
||||
|
||||
func signOutMastodonUser(authentication: MastodonAuthentication) async throws {
|
||||
try await AuthenticationServiceProvider.shared.delete(authentication: authentication)
|
||||
_ = try await AppContext.shared.apiService.cancelSubscription(domain: authentication.domain, authorization: authentication.authorization)
|
||||
_ = try await APIService.shared.cancelSubscription(domain: authentication.domain, authorization: authentication.authorization)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@ -217,13 +222,13 @@ public extension AuthenticationServiceProvider {
|
||||
userDefaults.didMigrateAuthentications == false
|
||||
}
|
||||
|
||||
func fetchAccounts(apiService: APIService) async {
|
||||
func fetchAccounts() async {
|
||||
// FIXME: This is a dirty hack to make the performance-stuff work.
|
||||
// Problem is, that we don't persist the user on disk anymore. So we have to fetch
|
||||
// it when we need it to display on the home timeline.
|
||||
// We need this (also) for the Account-list, but it might be the wrong place. App Startup might be more appropriate
|
||||
for authentication in authentications {
|
||||
guard let account = try? await apiService.accountInfo(domain: authentication.domain,
|
||||
guard let account = try? await APIService.shared.accountInfo(domain: authentication.domain,
|
||||
userID: authentication.userID,
|
||||
authorization: Mastodon.API.OAuth.Authorization(accessToken: authentication.userAccessToken)).value else { continue }
|
||||
|
||||
@ -250,7 +255,7 @@ private extension AuthenticationServiceProvider {
|
||||
_ previousFollowingIDs: [String]? = nil,
|
||||
_ maxID: String? = nil
|
||||
) async throws {
|
||||
let apiService = AppContext.shared.apiService
|
||||
let apiService = APIService.shared
|
||||
|
||||
let followingResponse = try await fetchFollowing(maxID, apiService, authBox)
|
||||
let followingIds = (previousFollowingIDs ?? []) + followingResponse.ids
|
||||
|
@ -169,29 +169,29 @@ private extension FeedDataController {
|
||||
func load(kind: MastodonFeed.Kind, maxID: MastodonStatus.ID?) async throws -> [MastodonFeed] {
|
||||
switch kind {
|
||||
case .home(let timeline):
|
||||
await AuthenticationServiceProvider.shared.fetchAccounts(apiService: context.apiService)
|
||||
await AuthenticationServiceProvider.shared.fetchAccounts()
|
||||
|
||||
let response: Mastodon.Response.Content<[Mastodon.Entity.Status]>
|
||||
|
||||
switch timeline {
|
||||
case .home:
|
||||
response = try await context.apiService.homeTimeline(
|
||||
response = try await APIService.shared.homeTimeline(
|
||||
maxID: maxID,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
case .public:
|
||||
response = try await context.apiService.publicTimeline(
|
||||
response = try await APIService.shared.publicTimeline(
|
||||
query: .init(local: true, maxID: maxID),
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
case let .list(id):
|
||||
response = try await context.apiService.listTimeline(
|
||||
response = try await APIService.shared.listTimeline(
|
||||
id: id,
|
||||
query: .init(maxID: maxID),
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
case let .hashtag(tag):
|
||||
response = try await context.apiService.hashtagTimeline(
|
||||
response = try await APIService.shared.hashtagTimeline(
|
||||
hashtag: tag,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
@ -209,10 +209,10 @@ private extension FeedDataController {
|
||||
|
||||
private func getFeeds(with scope: APIService.MastodonNotificationScope?, accountID: String? = nil) async throws -> [MastodonFeed] {
|
||||
|
||||
let notifications = try await context.apiService.notifications(maxID: nil, accountID: accountID, scope: scope, authenticationBox: authenticationBox).value
|
||||
let notifications = try await APIService.shared.notifications(maxID: nil, accountID: accountID, scope: scope, authenticationBox: authenticationBox).value
|
||||
|
||||
let accounts = notifications.map { $0.account }
|
||||
let relationships = try await context.apiService.relationship(forAccounts: accounts, authenticationBox: authenticationBox).value
|
||||
let relationships = try await APIService.shared.relationship(forAccounts: accounts, authenticationBox: authenticationBox).value
|
||||
|
||||
let notificationsWithRelationship: [(notification: Mastodon.Entity.Notification, relationship: Mastodon.Entity.Relationship?)] = notifications.compactMap { notification in
|
||||
guard let relationship = relationships.first(where: {$0.id == notification.account.id }) else { return (notification: notification, relationship: nil)}
|
||||
|
@ -22,7 +22,7 @@ public final class SettingFetchedResultController: NSObject {
|
||||
// output
|
||||
public let settings = CurrentValueSubject<[Setting], Never>([])
|
||||
|
||||
public init(managedObjectContext: NSManagedObjectContext, additionalPredicate: NSPredicate?) {
|
||||
public init(additionalPredicate: NSPredicate?) {
|
||||
self.fetchedResultsController = {
|
||||
let fetchRequest = Setting.sortedFetchRequest
|
||||
fetchRequest.returnsObjectsAsFaults = false
|
||||
@ -32,7 +32,7 @@ public final class SettingFetchedResultController: NSObject {
|
||||
fetchRequest.fetchBatchSize = 20
|
||||
let controller = NSFetchedResultsController(
|
||||
fetchRequest: fetchRequest,
|
||||
managedObjectContext: managedObjectContext,
|
||||
managedObjectContext: PersistenceManager.shared.managedObjectContext,
|
||||
sectionNameKeyPath: nil,
|
||||
cacheName: nil
|
||||
)
|
||||
|
@ -15,6 +15,9 @@ import AlamofireImage
|
||||
|
||||
public final class APIService {
|
||||
|
||||
@MainActor
|
||||
public static let shared = { APIService(backgroundContext: PersistenceManager.shared.backgroundManagedObjectContext) }()
|
||||
|
||||
public static let callbackURLScheme = "mastodon"
|
||||
public static let oauthCallbackURL = "mastodon://joinmastodon.org/oauth"
|
||||
|
||||
@ -23,15 +26,13 @@ public final class APIService {
|
||||
// internal
|
||||
let session: URLSession
|
||||
|
||||
// input
|
||||
public let backgroundManagedObjectContext: NSManagedObjectContext
|
||||
|
||||
// output
|
||||
public let error = PassthroughSubject<APIError, Never>()
|
||||
|
||||
public init(backgroundManagedObjectContext: NSManagedObjectContext) {
|
||||
self.backgroundManagedObjectContext = backgroundManagedObjectContext
|
||||
|
||||
private init(backgroundContext: NSManagedObjectContext) {
|
||||
backgroundManagedObjectContext = backgroundContext
|
||||
let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "Unknown"
|
||||
let configuration = URLSessionConfiguration.default
|
||||
configuration.httpAdditionalHeaders = ["User-Agent" : "mastodon-ios/" + appVersion]
|
||||
@ -48,6 +49,10 @@ public final class APIService {
|
||||
UIImageView.af.sharedImageDownloader = ImageDownloader(downloadPrioritization: .lifo)
|
||||
}
|
||||
|
||||
public static func isolatedService() -> APIService {
|
||||
let taskContext = PersistenceManager.shared.coreDataStack.newTaskContext()
|
||||
return APIService(backgroundContext: taskContext)
|
||||
}
|
||||
}
|
||||
|
||||
extension APIService {
|
||||
|
@ -37,7 +37,7 @@ extension EmojiService.CustomEmojiViewModel.LoadState {
|
||||
let authenticationBox = AuthenticationServiceProvider.shared.activeAuthentication,
|
||||
let stateMachine else { return }
|
||||
|
||||
let apiService = viewModel.service.apiService
|
||||
let apiService = APIService.shared
|
||||
|
||||
apiService.customEmoji(domain: viewModel.domain, authenticationBox: authenticationBox)
|
||||
// .receive(on: DispatchQueue.main)
|
||||
|
@ -10,15 +10,9 @@ import Combine
|
||||
import MastodonSDK
|
||||
|
||||
public final class EmojiService {
|
||||
let apiService: APIService
|
||||
|
||||
public static let shared = { EmojiService() }()
|
||||
let workingQueue = DispatchQueue(label: "org.joinmastodon.app.EmojiService.working-queue")
|
||||
private(set) var customEmojiViewModelDict: [String: CustomEmojiViewModel] = [:]
|
||||
|
||||
init(apiService: APIService) {
|
||||
self.apiService = apiService
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension EmojiService {
|
||||
|
@ -67,7 +67,7 @@ extension MastodonAttachmentService.UploadState {
|
||||
)
|
||||
|
||||
// and needs clone the `query` if needs retry
|
||||
service.context.apiService.uploadMedia(
|
||||
APIService.shared.uploadMedia(
|
||||
domain: authenticationBox.domain,
|
||||
query: query,
|
||||
mastodonAuthenticationBox: authenticationBox,
|
||||
@ -126,7 +126,7 @@ extension MastodonAttachmentService.UploadState {
|
||||
return
|
||||
}
|
||||
|
||||
service.context.apiService.getMedia(
|
||||
APIService.shared.getMedia(
|
||||
attachmentID: attachment.id,
|
||||
mastodonAuthenticationBox: authenticationBox
|
||||
)
|
||||
|
@ -15,6 +15,8 @@ import MastodonLocalization
|
||||
|
||||
public final class NotificationService {
|
||||
|
||||
public static let shared = { NotificationService() }()
|
||||
|
||||
public static let unreadShortcutItemIdentifier = "org.joinmastodon.app.NotificationService.unread-shortcut"
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
@ -22,7 +24,6 @@ public final class NotificationService {
|
||||
let workingQueue = DispatchQueue(label: "org.joinmastodon.app.NotificationService.working-queue")
|
||||
|
||||
// input
|
||||
weak var apiService: APIService?
|
||||
public let isNotificationPermissionGranted = CurrentValueSubject<Bool, Never>(false)
|
||||
public let deviceToken = CurrentValueSubject<Data?, Never>(nil)
|
||||
public let applicationIconBadgeNeedsUpdate = CurrentValueSubject<Void, Never>(Void())
|
||||
@ -33,11 +34,7 @@ public final class NotificationService {
|
||||
public let unreadNotificationCountDidUpdate = CurrentValueSubject<Void, Never>(Void())
|
||||
public let requestRevealNotificationPublisher = PassthroughSubject<MastodonPushNotification, Never>()
|
||||
|
||||
init(
|
||||
apiService: APIService
|
||||
) {
|
||||
self.apiService = apiService
|
||||
|
||||
private init() {
|
||||
AuthenticationServiceProvider.shared.$authentications
|
||||
.sink(receiveValue: { [weak self] mastodonAuthentications in
|
||||
guard let self = self else { return }
|
||||
@ -171,10 +168,9 @@ extension NotificationService {
|
||||
private func fetchLatestNotifications(
|
||||
pushNotification: MastodonPushNotification
|
||||
) async throws {
|
||||
guard let apiService = apiService else { return }
|
||||
guard let authenticationBox = try await authenticationBox(for: pushNotification) else { return }
|
||||
|
||||
_ = try await apiService.notifications(
|
||||
_ = try await APIService.shared.notifications(
|
||||
maxID: nil,
|
||||
scope: .everything,
|
||||
authenticationBox: authenticationBox
|
||||
@ -186,8 +182,7 @@ extension NotificationService {
|
||||
) async throws {
|
||||
// Subscription maybe failed to cancel when sign-out
|
||||
// Try cancel again if receive that kind push notification
|
||||
let managedObjectContext = AppContext.shared.managedObjectContext
|
||||
guard let apiService = apiService else { return }
|
||||
let managedObjectContext = PersistenceManager.shared.managedObjectContext
|
||||
|
||||
let userAccessToken = pushNotification.accessToken
|
||||
|
||||
@ -204,7 +199,7 @@ extension NotificationService {
|
||||
guard let domain = try await domain(for: pushNotification) else { return }
|
||||
|
||||
do {
|
||||
_ = try await apiService.cancelSubscription(
|
||||
_ = try await APIService.shared.cancelSubscription(
|
||||
domain: domain,
|
||||
authorization: .init(accessToken: userAccessToken)
|
||||
)
|
||||
@ -213,7 +208,7 @@ extension NotificationService {
|
||||
}
|
||||
|
||||
private func domain(for pushNotification: MastodonPushNotification) async throws -> String? {
|
||||
let managedObjectContext = AppContext.shared.managedObjectContext
|
||||
let managedObjectContext = PersistenceManager.shared.managedObjectContext
|
||||
return try await managedObjectContext.perform {
|
||||
let subscriptionRequest = NotificationSubscription.sortedFetchRequest
|
||||
subscriptionRequest.predicate = NotificationSubscription.predicate(userToken: pushNotification.accessToken)
|
||||
|
@ -12,6 +12,7 @@ import Alamofire
|
||||
import AlamofireImage
|
||||
|
||||
public final class PhotoLibraryService: NSObject {
|
||||
public static let shared = { PhotoLibraryService() }()
|
||||
|
||||
}
|
||||
|
||||
|
@ -10,11 +10,10 @@ import Combine
|
||||
|
||||
public final class PublisherService {
|
||||
|
||||
public static let shared = { PublisherService() }()
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
// input
|
||||
let apiService: APIService
|
||||
|
||||
@Published public private(set) var statusPublishers: [StatusPublisher] = []
|
||||
|
||||
// output
|
||||
@ -23,11 +22,7 @@ public final class PublisherService {
|
||||
var currentPublishProgressObservation: NSKeyValueObservation?
|
||||
@Published public var currentPublishProgress: Double = 0
|
||||
|
||||
public init(
|
||||
apiService: APIService
|
||||
) {
|
||||
self.apiService = apiService
|
||||
|
||||
private init() {
|
||||
$statusPublishers
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] publishers in
|
||||
@ -84,7 +79,7 @@ extension PublisherService {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let result = try await publisher.publish(api: apiService, authenticationBox: authenticationBox)
|
||||
let result = try await publisher.publish(api: APIService.shared, authenticationBox: authenticationBox)
|
||||
|
||||
self.statusPublishResult.send(.success(result))
|
||||
self.statusPublishers.removeAll(where: { $0 === publisher })
|
||||
|
@ -15,24 +15,20 @@ import MastodonCommon
|
||||
|
||||
public final class SettingService {
|
||||
|
||||
public static let shared = { SettingService() }()
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
// input
|
||||
weak var apiService: APIService?
|
||||
weak var notificationService: NotificationService?
|
||||
|
||||
var apiService: APIService { APIService.shared }
|
||||
var notificationService: NotificationService { NotificationService.shared
|
||||
}
|
||||
// output
|
||||
let settingFetchedResultController: SettingFetchedResultController
|
||||
public let currentSetting = CurrentValueSubject<Setting?, Never>(nil)
|
||||
|
||||
init(
|
||||
apiService: APIService,
|
||||
notificationService: NotificationService
|
||||
) {
|
||||
self.apiService = apiService
|
||||
self.notificationService = notificationService
|
||||
private init() {
|
||||
self.settingFetchedResultController = SettingFetchedResultController(
|
||||
managedObjectContext: AppContext.shared.managedObjectContext,
|
||||
additionalPredicate: nil
|
||||
)
|
||||
|
||||
@ -41,7 +37,7 @@ public final class SettingService {
|
||||
.compactMap { [weak self] mastodonAuthenticationBoxes -> AnyPublisher<[MastodonAuthenticationBox], Never>? in
|
||||
guard let self = self else { return nil }
|
||||
|
||||
let managedObjectContext = AppContext.shared.backgroundManagedObjectContext
|
||||
let managedObjectContext = PersistenceManager.shared.backgroundManagedObjectContext
|
||||
return managedObjectContext.performChanges {
|
||||
for authenticationBox in mastodonAuthenticationBoxes {
|
||||
let domain = authenticationBox.domain
|
||||
@ -95,7 +91,7 @@ public final class SettingService {
|
||||
guard setting.domain == authenticationBox.domain,
|
||||
setting.userID == authenticationBox.userID else { return nil }
|
||||
|
||||
let _viewModel = self.notificationService?.dequeueNotificationViewModel(
|
||||
let _viewModel = notificationService.dequeueNotificationViewModel(
|
||||
mastodonAuthenticationBox: authenticationBox
|
||||
)
|
||||
guard let viewModel = _viewModel else { return nil }
|
||||
|
@ -13,21 +13,17 @@ import MastodonSDK
|
||||
import MastodonMeta
|
||||
|
||||
public final class StatusFilterService {
|
||||
public static let shared = { StatusFilterService() }()
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
// input
|
||||
weak var apiService: APIService?
|
||||
public let filterUpdatePublisher = PassthroughSubject<Void, Never>()
|
||||
|
||||
// output
|
||||
@Published public var activeFilters: [Mastodon.Entity.Filter] = []
|
||||
|
||||
init(
|
||||
apiService: APIService
|
||||
) {
|
||||
self.apiService = apiService
|
||||
|
||||
private init() {
|
||||
// fetch account filters every 300s
|
||||
// also trigger fetch when app resume from background
|
||||
let filterUpdateTimerPublisher = Timer.publish(every: 300.0, on: .main, in: .common)
|
||||
@ -48,7 +44,7 @@ public final class StatusFilterService {
|
||||
guard let box = mastodonAuthenticationBoxes.first else {
|
||||
return Just(Result { throw APIService.APIError.implicit(.authenticationMissing) }).eraseToAnyPublisher()
|
||||
}
|
||||
return apiService.filters(mastodonAuthenticationBox: box)
|
||||
return APIService.shared.filters(mastodonAuthenticationBox: box)
|
||||
.map { response in
|
||||
let now = Date()
|
||||
let newResponse = response.map { filters in
|
||||
|
@ -77,7 +77,7 @@ extension AttachmentViewModel {
|
||||
do {
|
||||
let result = try await upload(
|
||||
context: .init(
|
||||
apiService: self.api,
|
||||
apiService: APIService.shared,
|
||||
authenticationBox: self.authenticationBox
|
||||
),
|
||||
isRetry: isRetry
|
||||
|
@ -41,7 +41,6 @@ final public class AttachmentViewModel: NSObject, ObservableObject, Identifiable
|
||||
}()
|
||||
|
||||
// input
|
||||
public let api: APIService
|
||||
public let authenticationBox: MastodonAuthenticationBox
|
||||
public let input: Input
|
||||
public let sizeLimit: SizeLimit
|
||||
@ -72,7 +71,6 @@ final public class AttachmentViewModel: NSObject, ObservableObject, Identifiable
|
||||
@Published var remainTimeLocalizedString: String?
|
||||
|
||||
public init(
|
||||
api: APIService,
|
||||
authenticationBox: MastodonAuthenticationBox,
|
||||
input: Input,
|
||||
sizeLimit: SizeLimit,
|
||||
@ -80,7 +78,6 @@ final public class AttachmentViewModel: NSObject, ObservableObject, Identifiable
|
||||
isEditing: Bool = false,
|
||||
caption: String? = nil
|
||||
) {
|
||||
self.api = api
|
||||
self.authenticationBox = authenticationBox
|
||||
self.input = input
|
||||
self.sizeLimit = sizeLimit
|
||||
|
@ -130,7 +130,7 @@ extension AutoCompleteViewModel.State {
|
||||
)
|
||||
|
||||
do {
|
||||
let response = try await viewModel.context.apiService.search(
|
||||
let response = try await APIService.shared.search(
|
||||
query: query,
|
||||
authenticationBox: viewModel.authenticationBox
|
||||
)
|
||||
|
@ -40,7 +40,7 @@ final class AutoCompleteViewModel {
|
||||
init(context: AppContext, authenticationBox: MastodonAuthenticationBox) {
|
||||
self.context = context
|
||||
self.authenticationBox = authenticationBox
|
||||
self.customEmojiViewModel = context.emojiService.dequeueCustomEmojiViewModel(for: authenticationBox.domain)
|
||||
self.customEmojiViewModel = EmojiService.shared.dequeueCustomEmojiViewModel(for: authenticationBox.domain)
|
||||
// end init
|
||||
|
||||
autoCompleteItems
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user