diff --git a/Gemfile.lock b/Gemfile.lock index 873f725e5..e0ed91c5b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,6 +100,7 @@ GEM PLATFORMS arm64-darwin-21 + x86_64-darwin-21 DEPENDENCIES arkana diff --git a/Localization/app.json b/Localization/app.json index a965b23ae..c44029040 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -180,7 +180,9 @@ "unmute": "Unmute", "unmute_user": "Unmute %s", "muted": "Muted", - "edit_info": "Edit Info" + "edit_info": "Edit Info", + "show_reblogs": "Show Reblogs", + "hide_reblogs": "Hide Reblogs", }, "timeline": { "filtered": "Filtered", @@ -455,7 +457,15 @@ "confirm_unblock_user": { "title": "Unblock Account", "message": "Confirm to unblock %s" - } + }, + "confirm_show_reblogs": { + "title": "Show Reblogs", + "message": "Confirm to show reblogs" + }, + "confirm_hide_reblogs": { + "title": "Hide Reblogs", + "message": "Confirm to hide reblogs" + }, }, "accessibility": { "show_avatar_image": "Show avatar image", @@ -686,4 +696,4 @@ "accessibility_hint": "Double tap to dismiss this wizard" } } -} \ No newline at end of file +} diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index 34ffd227b..3d914a3f1 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Alamofire/Alamofire.git", "state" : { - "revision" : "354dda32d89fc8cd4f5c46487f64957d355f53d8", - "version" : "5.6.1" + "revision" : "8dd85aee02e39dd280c75eef88ffdb86eed4b07b", + "version" : "5.6.2" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Flipboard/FLAnimatedImage.git", "state" : { - "revision" : "e7f9fd4681ae41bf6f3056db08af4f401d61da52", - "version" : "1.0.16" + "revision" : "d4f07b6f164d53c1212c3e54d6460738b1981e9f", + "version" : "1.0.17" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kean/Nuke.git", "state" : { - "revision" : "0ea7545b5c918285aacc044dc75048625c8257cc", - "version" : "10.8.0" + "revision" : "a002b7fd786f2df2ed4333fe73a9727499fd9d97", + "version" : "10.11.2" } }, { @@ -113,8 +113,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/uias/Pageboy", "state" : { - "revision" : "34ecb6e7c4e0e07494960ab2f7cc9a02293915a6", - "version" : "3.6.2" + "revision" : "af8fa81788b893205e1ff42ddd88c5b0b315d7c5", + "version" : "3.7.0" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SDWebImage/SDWebImage.git", "state" : { - "revision" : "2e63d0061da449ad0ed130768d05dceb1496de44", - "version" : "5.12.5" + "revision" : "318cca556b0489aede0cd98d8d0c7f1408ab7bd6", + "version" : "5.13.5" } }, { @@ -176,8 +176,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/scinfu/SwiftSoup.git", "state" : { - "revision" : "41e7c263fb8c277e980ebcb9b0b5f6031d3d4886", - "version" : "2.4.2" + "revision" : "6778575285177365cbad3e5b8a72f2a20583cfec", + "version" : "2.4.3" } }, { @@ -189,15 +189,6 @@ "version" : "0.1.4" } }, - { - "identity" : "swiftyjson", - "kind" : "remoteSourceControl", - "location" : "https://github.com/SwiftyJSON/SwiftyJSON.git", - "state" : { - "revision" : "b3dcd7dbd0d488e1a7077cb33b00f2083e382f07", - "version" : "5.0.1" - } - }, { "identity" : "tabbarpager", "kind" : "remoteSourceControl", 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 9d9e73eb3..332ed75e4 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift @@ -205,6 +205,45 @@ extension DataSourceFacade { menuContext: MenuContext ) async throws { switch action { + case .hideReblogs(let actionContext): + let title = actionContext.showReblogs ? L10n.Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.title : L10n.Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.title + let message = actionContext.showReblogs ? L10n.Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.message : L10n.Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.message + + let alertController = UIAlertController( + title: title, + message: message, + preferredStyle: .alert + ) + + let actionTitle = actionContext.showReblogs ? L10n.Common.Controls.Friendship.hideReblogs : L10n.Common.Controls.Friendship.showReblogs + let showHideReblogsAction = UIAlertAction( + title: actionTitle, + style: .destructive + ) { [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: L10n.Common.Controls.Actions.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, @@ -230,9 +269,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, @@ -258,9 +297,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 } @@ -349,9 +388,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 @@ -371,3 +410,4 @@ extension DataSourceFacade { } } + diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index 9dd06b22c..3ce1fd33a 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -376,13 +376,22 @@ extension ProfileViewController { } let name = user.displayNameWithFallback let _ = ManagedObjectRecord(objectID: user.objectID) + + var menuActions: [MastodonMenu.Action] = [ + .muteUser(.init(name: name, isMuting: self.viewModel.relationshipViewModel.isMuting)), + .blockUser(.init(name: name, isBlocking: self.viewModel.relationshipViewModel.isBlocking)), + .reportUser(.init(name: name)), + .shareUser(.init(name: name)), + ] + + if let me = self.viewModel?.me, me.following.contains(user) { + let showReblogs = me.showingReblogsBy.contains(user) + let context = MastodonMenu.HideReblogsActionContext(showReblogs: showReblogs) + menuActions.insert(.hideReblogs(context), at: 1) + } + let menu = MastodonMenu.setupMenu( - actions: [ - .muteUser(.init(name: name, isMuting: self.viewModel.relationshipViewModel.isMuting)), - .blockUser(.init(name: name, isBlocking: self.viewModel.relationshipViewModel.isBlocking)), - .reportUser(.init(name: name)), - .shareUser(.init(name: name)), - ], + actions: menuActions, delegate: self ) return menu @@ -397,7 +406,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) } @@ -740,7 +751,7 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate { let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.EditProfileFailure.title, preferredStyle: .alert) let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil) alertController.addAction(okAction) - self.coordinator.present( + _ = self.coordinator.present( scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil) @@ -763,11 +774,11 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate { break case .follow, .request, .pending, .following: guard let user = viewModel.user else { return } - let reocrd = ManagedObjectRecord(objectID: user.objectID) + let record = ManagedObjectRecord(objectID: user.objectID) Task { try await DataSourceFacade.responseToUserFollowAction( dependency: self, - user: reocrd + user: record ) } case .muting: @@ -816,10 +827,8 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate { let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil) alertController.addAction(cancelAction) present(alertController, animated: true, completion: nil) - case .blocked: + case .blocked, .showReblogs, .isMyself,.followingBy, .blockingBy, .suspended, .edit, .editing, .updating: break - default: - assertionFailure() } } diff --git a/MastodonSDK/Package.swift b/MastodonSDK/Package.swift index 852817c20..ca241038b 100644 --- a/MastodonSDK/Package.swift +++ b/MastodonSDK/Package.swift @@ -40,7 +40,6 @@ let package = Package( .package(url: "https://github.com/MainasuK/CommonOSLog", from: "0.1.1"), .package(url: "https://github.com/MainasuK/FPSIndicator.git", from: "1.0.0"), .package(url: "https://github.com/slackhq/PanModal.git", from: "1.2.7"), - .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "5.0.0"), .package(url: "https://github.com/TimOliver/TOCropViewController.git", from: "2.6.1"), .package(url: "https://github.com/TwidereProject/MetaTextKit.git", exact: "2.2.5"), .package(url: "https://github.com/TwidereProject/TabBarPager.git", from: "0.1.0"), @@ -103,7 +102,6 @@ let package = Package( .target( name: "MastodonSDK", dependencies: [ - .product(name: "SwiftyJSON", package: "SwiftyJSON"), .product(name: "NIOHTTP1", package: "swift-nio"), ] ), diff --git a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/.xccurrentversion b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/.xccurrentversion index cdd244c9c..1d5ea989f 100644 --- a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/.xccurrentversion +++ b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - CoreData 3.xcdatamodel + CoreData 4.xcdatamodel diff --git a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 4.xcdatamodel/contents b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 4.xcdatamodel/contents new file mode 100644 index 000000000..7604028f1 --- /dev/null +++ b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 4.xcdatamodel/contents @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/MastodonUser.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/MastodonUser.swift index 85e844b09..760985d68 100644 --- a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/MastodonUser.swift +++ b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/MastodonUser.swift @@ -89,7 +89,8 @@ final public class MastodonUser: NSManagedObject { @NSManaged public private(set) var endorsedBy: Set @NSManaged public private(set) var domainBlocking: Set @NSManaged public private(set) var domainBlockingBy: Set - + @NSManaged public private(set) var showingReblogs: Set + @NSManaged public private(set) var showingReblogsBy: Set } extension MastodonUser { @@ -521,6 +522,7 @@ extension MastodonUser: AutoUpdatableObject { } } } + public func update(isDomainBlocking: Bool, by mastodonUser: MastodonUser) { if isDomainBlocking { if !self.domainBlockingBy.contains(mastodonUser) { @@ -533,4 +535,15 @@ extension MastodonUser: AutoUpdatableObject { } } + public func update(isShowingReblogs: Bool, by mastodonUser: MastodonUser) { + if isShowingReblogs { + if !self.showingReblogsBy.contains(mastodonUser) { + self.mutableSetValue(forKey: #keyPath(MastodonUser.showingReblogsBy)).add(mastodonUser) + } + } else { + if self.showingReblogsBy.contains(mastodonUser) { + self.mutableSetValue(forKey: #keyPath(MastodonUser.showingReblogsBy)).remove(mastodonUser) + } + } + } } diff --git a/MastodonSDK/Sources/MastodonCore/Persistence/Persistence+MastodonUser.swift b/MastodonSDK/Sources/MastodonCore/Persistence/Persistence+MastodonUser.swift index eb69c36d1..8571a11cf 100644 --- a/MastodonSDK/Sources/MastodonCore/Persistence/Persistence+MastodonUser.swift +++ b/MastodonSDK/Sources/MastodonCore/Persistence/Persistence+MastodonUser.swift @@ -157,5 +157,6 @@ extension Persistence.MastodonUser { user.update(isBlocking: relationship.blocking, by: me) relationship.domainBlocking.flatMap { user.update(isDomainBlocking: $0, by: me) } relationship.blockedBy.flatMap { me.update(isBlocking: $0, by: user) } + relationship.showingReblogs.flatMap { me.update(isShowingReblogs: $0, by: user) } } } diff --git a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Follow.swift b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Follow.swift index cfb5b8ee2..442d293ce 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Follow.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Follow.swift @@ -13,7 +13,7 @@ import CommonOSLog import MastodonSDK extension APIService { - + private struct MastodonFollowContext { let sourceUserID: MastodonUser.ID let targetUserID: MastodonUser.ID @@ -121,5 +121,56 @@ 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), + let authentication = authenticationBox.authenticationRecord.object(in: managedObjectContext) + else { throw APIError.implicit(.badRequest) } + + let me = authentication.user + let result: Result, Error> + + let oldShowReblogs = me.showingReblogsBy.contains(user) + let newShowReblogs = (oldShowReblogs == false) + + do { + let response = try await Mastodon.API.Account.follow( + session: session, + domain: authenticationBox.domain, + accountID: user.id, + followQueryType: .follow(query: .init(reblogs: newShowReblogs)), + 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() + } } diff --git a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift index c64a50fa2..52ed59c09 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift +++ b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift @@ -198,6 +198,8 @@ public enum L10n { public static let follow = L10n.tr("Localizable", "Common.Controls.Friendship.Follow") /// Following public static let following = L10n.tr("Localizable", "Common.Controls.Friendship.Following") + /// Hide Reblogs + public static let hideReblogs = L10n.tr("Localizable", "Common.Controls.Friendship.HideReblogs") /// Mute public static let mute = L10n.tr("Localizable", "Common.Controls.Friendship.Mute") /// Muted @@ -210,6 +212,8 @@ public enum L10n { public static let pending = L10n.tr("Localizable", "Common.Controls.Friendship.Pending") /// Request public static let request = L10n.tr("Localizable", "Common.Controls.Friendship.Request") + /// Show Reblogs + public static let showReblogs = L10n.tr("Localizable", "Common.Controls.Friendship.ShowReblogs") /// Unblock public static let unblock = L10n.tr("Localizable", "Common.Controls.Friendship.Unblock") /// Unblock %@ @@ -709,6 +713,12 @@ public enum L10n { /// Block Account public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.Title") } + public enum ConfirmHideReblogs { + /// Confirm to hide reblogs + public static let message = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Message") + /// Hide reblogs + public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Title") + } public enum ConfirmMuteUser { /// Confirm to mute %@ public static func message(_ p1: Any) -> String { @@ -717,6 +727,12 @@ public enum L10n { /// Mute Account public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmMuteUser.Title") } + public enum ConfirmShowReblogs { + /// Confirm to show reblogs + public static let message = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Message") + /// Show Reblogs + public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Title") + } public enum ConfirmUnblockUser { /// Confirm to unblock %@ public static func message(_ p1: Any) -> String { diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings index 1c40bf855..6917eb0c7 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings @@ -77,6 +77,9 @@ Please check your internet connection."; "Common.Controls.Friendship.UnblockUser" = "Unblock %@"; "Common.Controls.Friendship.Unmute" = "Unmute"; "Common.Controls.Friendship.UnmuteUser" = "Unmute %@"; +"Common.Controls.Friendship.HideReblogs" = "Hide Reblogs"; +"Common.Controls.Friendship.ShowReblogs" = "Show Reblogs"; + "Common.Controls.Keyboard.Common.ComposeNewPost" = "Compose New Post"; "Common.Controls.Keyboard.Common.OpenSettings" = "Open Settings"; "Common.Controls.Keyboard.Common.ShowFavorites" = "Show Favorites"; @@ -262,6 +265,10 @@ uploaded to Mastodon."; "Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.Title" = "Unblock Account"; "Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Message" = "Confirm to unmute %@"; "Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Title" = "Unmute Account"; +"Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Message" = "Confirm to show reblogs"; +"Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Title" = "Show Reblogs"; +"Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Message" = "Confirm to hide reblogs"; +"Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Title" = "Hide reblogs"; "Scene.Profile.SegmentedControl.About" = "About"; "Scene.Profile.SegmentedControl.Media" = "Media"; "Scene.Profile.SegmentedControl.Posts" = "Posts"; diff --git a/MastodonSDK/Sources/MastodonUI/View/Menu/MastodonMenu.swift b/MastodonSDK/Sources/MastodonUI/View/Menu/MastodonMenu.swift index d6a5bdbac..422494328 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Menu/MastodonMenu.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Menu/MastodonMenu.swift @@ -45,11 +45,23 @@ extension MastodonMenu { case reportUser(ReportUserActionContext) case shareUser(ShareUserActionContext) case bookmarkStatus(BookmarkStatusActionContext) + case hideReblogs(HideReblogsActionContext) case shareStatus case deleteStatus func build(delegate: MastodonMenuDelegate) -> BuiltAction { switch self { + case .hideReblogs(let context): + let title = context.showReblogs ? L10n.Common.Controls.Friendship.hideReblogs : L10n.Common.Controls.Friendship.showReblogs + let reblogAction = BuiltAction( + title: title, + image: UIImage(systemName: "arrow.2.squarepath") + ) { [weak delegate] in + guard let delegate = delegate else { return } + delegate.menuAction(self) + } + + return reblogAction case .muteUser(let context): let muteAction = BuiltAction( title: context.isMuting ? L10n.Common.Controls.Friendship.unmuteUser(context.name) : L10n.Common.Controls.Friendship.muteUser(context.name), @@ -205,5 +217,12 @@ extension MastodonMenu { self.name = name } } - + + public struct HideReblogsActionContext { + public let showReblogs: Bool + + public init(showReblogs: Bool) { + self.showReblogs = showReblogs + } + } } diff --git a/MastodonSDK/Sources/MastodonUI/ViewModel/RelationshipViewModel.swift b/MastodonSDK/Sources/MastodonUI/ViewModel/RelationshipViewModel.swift index a19de5138..99cff19be 100644 --- a/MastodonSDK/Sources/MastodonUI/ViewModel/RelationshipViewModel.swift +++ b/MastodonSDK/Sources/MastodonUI/ViewModel/RelationshipViewModel.swift @@ -12,6 +12,7 @@ import MastodonLocalization import CoreDataStack public enum RelationshipAction: Int, CaseIterable { + case showReblogs case isMyself case followingBy case blockingBy @@ -27,7 +28,7 @@ public enum RelationshipAction: Int, CaseIterable { case edit case editing case updating - + public var option: RelationshipActionOptionSet { return RelationshipActionOptionSet(rawValue: 1 << rawValue) } @@ -57,7 +58,7 @@ public struct RelationshipActionOptionSet: OptionSet { public static let edit = RelationshipAction.edit.option public static let editing = RelationshipAction.editing.option public static let updating = RelationshipAction.updating.option - + public static let showReblogs = RelationshipAction.showReblogs.option public static let editOptions: RelationshipActionOptionSet = [.edit, .editing, .updating] public func highPriorityAction(except: RelationshipActionOptionSet) -> RelationshipAction? { @@ -75,24 +76,24 @@ public struct RelationshipActionOptionSet: OptionSet { return " " } switch highPriorityAction { - case .isMyself: return "" - case .followingBy: return " " - case .blockingBy: return " " - case .none: return " " - case .follow: return L10n.Common.Controls.Friendship.follow - case .request: return L10n.Common.Controls.Friendship.request - case .pending: return L10n.Common.Controls.Friendship.pending - case .following: return L10n.Common.Controls.Friendship.following - case .muting: return L10n.Common.Controls.Friendship.muted - case .blocked: return L10n.Common.Controls.Friendship.follow // blocked by user (deprecated) - case .blocking: return L10n.Common.Controls.Friendship.blocked - case .suspended: return L10n.Common.Controls.Friendship.follow - case .edit: return L10n.Common.Controls.Friendship.editInfo - case .editing: return L10n.Common.Controls.Actions.done - case .updating: return " " + case .isMyself: return "" + case .followingBy: return " " + case .blockingBy: return " " + case .none: return " " + case .follow: return L10n.Common.Controls.Friendship.follow + case .request: return L10n.Common.Controls.Friendship.request + case .pending: return L10n.Common.Controls.Friendship.pending + case .following: return L10n.Common.Controls.Friendship.following + case .muting: return L10n.Common.Controls.Friendship.muted + case .blocked: return L10n.Common.Controls.Friendship.follow // blocked by user (deprecated) + case .blocking: return L10n.Common.Controls.Friendship.blocked + case .suspended: return L10n.Common.Controls.Friendship.follow + case .edit: return L10n.Common.Controls.Friendship.editInfo + case .editing: return L10n.Common.Controls.Actions.done + case .updating: return " " + case .showReblogs: return " " } } - } public final class RelationshipViewModel { @@ -114,6 +115,7 @@ public final class RelationshipViewModel { @Published public var isFollowing = false @Published public var isFollowingBy = false @Published public var isMuting = false + @Published public var showReblogs = false @Published public var isBlocking = false @Published public var isBlockingBy = false @Published public var isSuspended = false @@ -184,6 +186,7 @@ extension RelationshipViewModel { self.isBlockingBy = optionSet.contains(.blockingBy) self.isBlocking = optionSet.contains(.blocking) self.isSuspended = optionSet.contains(.suspended) + self.showReblogs = optionSet.contains(.showReblogs) self.optionSet = optionSet } @@ -196,6 +199,7 @@ extension RelationshipViewModel { isBlockingBy = false isBlocking = false optionSet = nil + showReblogs = false } } @@ -214,7 +218,8 @@ extension RelationshipViewModel { let isMuting = user.mutingBy.contains(me) let isBlockingBy = me.blockingBy.contains(user) let isBlocking = user.blockingBy.contains(me) - + let isShowingReblogs = me.showingReblogsBy.contains(user) + var optionSet: RelationshipActionOptionSet = [.follow] if isMyself { @@ -252,7 +257,11 @@ extension RelationshipViewModel { if user.suspended { optionSet.insert(.suspended) } - + + if isShowingReblogs { + optionSet.insert(.showReblogs) + } + return optionSet } }