diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift index c6e40e7d9..b3812f198 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift @@ -132,3 +132,14 @@ extension DataSourceFacade { } } // end func } + +extension DataSourceFacade { + static func responseToShowHideReblogAction( + dependency: NeedsDependency & AuthContextProvider, + user: ManagedObjectRecord + ) async throws { + _ = try await dependency.context.apiService.toggleShowReblogs( + for: user, + authenticationBox: dependency.authContext.mastodonAuthenticationBox) + } +} diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift index 412079293..ff41cc126 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift @@ -205,9 +205,42 @@ extension DataSourceFacade { menuContext: MenuContext ) async throws { switch action { - case .hideReblogs(_): - //TODO: Implement. Alert. Toggle on Server. - return + case .hideReblogs(let actionContext): + //FIXME: Add localized strings + let alertController = UIAlertController( + title: actionContext.showReblogs ? "Really hide?" : "Really show?", + message: actionContext.showReblogs ? "Really??" : "Really??", + preferredStyle: .alert + ) + + let showHideReblogsAction = UIAlertAction( + title: actionContext.showReblogs ? "Show" : "Hide", + style: .default + ) { [weak dependency] _ in + guard let dependency else { return } + + Task { + let managedObjectContext = dependency.context.managedObjectContext + let _user: ManagedObjectRecord? = try? await managedObjectContext.perform { + guard let user = menuContext.author?.object(in: managedObjectContext) else { return nil } + return ManagedObjectRecord(objectID: user.objectID) + } + + guard let user = _user else { return } + + try await DataSourceFacade.responseToShowHideReblogAction( + dependency: dependency, + user: user + ) + } + } + + alertController.addAction(showHideReblogsAction) + + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) + alertController.addAction(cancelAction) + + dependency.present(alertController, animated: true) case .muteUser(let actionContext): let alertController = UIAlertController( title: actionContext.isMuting ? L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.title : L10n.Scene.Profile.RelationshipActionAlert.ConfirmMuteUser.title, @@ -233,9 +266,9 @@ extension DataSourceFacade { } // end Task } alertController.addAction(confirmAction) - let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil) + let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel) alertController.addAction(cancelAction) - dependency.present(alertController, animated: true, completion: nil) + dependency.present(alertController, animated: true) case .blockUser(let actionContext): let alertController = UIAlertController( title: actionContext.isBlocking ? L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.title : L10n.Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.title, @@ -261,9 +294,9 @@ extension DataSourceFacade { } // end Task } alertController.addAction(confirmAction) - let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil) + let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel) alertController.addAction(cancelAction) - dependency.present(alertController, animated: true, completion: nil) + dependency.present(alertController, animated: true) case .reportUser: Task { guard let user = menuContext.author else { return } @@ -352,9 +385,9 @@ extension DataSourceFacade { } // end Task } alertController.addAction(confirmAction) - let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil) + let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel) alertController.addAction(cancelAction) - dependency.present(alertController, animated: true, completion: nil) + dependency.present(alertController, animated: true) } } // end func @@ -374,3 +407,4 @@ extension DataSourceFacade { } } + diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index 34c2775c8..dcaefccb7 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -378,8 +378,8 @@ extension ProfileViewController { let _ = ManagedObjectRecord(objectID: user.objectID) let menu = MastodonMenu.setupMenu( actions: [ - .hideReblogs(.init(showReblogs: self.viewModel.relationshipViewModel.showReblogs)), .muteUser(.init(name: name, isMuting: self.viewModel.relationshipViewModel.isMuting)), + .hideReblogs(.init(showReblogs: self.viewModel.relationshipViewModel.showReblogs)), .blockUser(.init(name: name, isBlocking: self.viewModel.relationshipViewModel.isBlocking)), .reportUser(.init(name: name)), .shareUser(.init(name: name)), @@ -398,7 +398,9 @@ extension ProfileViewController { } } receiveValue: { [weak self] menu in guard let self = self else { return } - self.moreMenuBarButtonItem.menu = menu + OperationQueue.main.addOperation { + self.moreMenuBarButtonItem.menu = menu + } } .store(in: &disposeBag) } diff --git a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Follow.swift b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Follow.swift index cfb5b8ee2..38c49970c 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Follow.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Follow.swift @@ -13,13 +13,14 @@ import CommonOSLog import MastodonSDK extension APIService { - + private struct MastodonFollowContext { let sourceUserID: MastodonUser.ID let targetUserID: MastodonUser.ID let isFollowing: Bool let isPending: Bool let needsUnfollow: Bool + let showsReblogs: Bool } /// Toggle friendship between target MastodonUser and current MastodonUser @@ -121,5 +122,52 @@ extension APIService { let response = try result.get() return response } - + + public func toggleShowReblogs( + for user: ManagedObjectRecord, + authenticationBox: MastodonAuthenticationBox + ) async throws -> Mastodon.Response.Content { + + let managedObjectContext = backgroundManagedObjectContext + guard let user = user.object(in: managedObjectContext) else { throw APIError.implicit(.badRequest) } + + let result: Result, Error> + let showReblogs = false //FIXME: Use showReblogs-value from data + let oldShowReblogs = true + + do { + let response = try await Mastodon.API.Account.follow( + session: session, + domain: authenticationBox.domain, + accountID: user.id, + followQueryType: .follow(query: .init(reblogs: showReblogs)), + authorization: authenticationBox.userAuthorization + ).singleOutput() + + result = .success(response) + } catch { + result = .failure(error) + } + + try await managedObjectContext.performChanges { + guard let me = authenticationBox.authenticationRecord.object(in: managedObjectContext)?.user else { return } + + switch result { + case .success(let response): + Persistence.MastodonUser.update( + mastodonUser: user, + context: Persistence.MastodonUser.RelationshipContext( + entity: response.value, + me: me, + networkDate: response.networkDate + ) + ) + case .failure: + // rollback + user.update(isShowingReblogs: oldShowReblogs, by: me) + } + } + + return try result.get() + } }