Merge remote-tracking branch 'upstream/develop' into account-switcher-a11y
This commit is contained in:
commit
0310508a53
|
@ -100,6 +100,7 @@ GEM
|
|||
|
||||
PLATFORMS
|
||||
arm64-darwin-21
|
||||
x86_64-darwin-21
|
||||
|
||||
DEPENDENCIES
|
||||
arkana
|
||||
|
|
|
@ -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,6 +457,14 @@
|
|||
"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": {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -132,3 +132,14 @@ extension DataSourceFacade {
|
|||
}
|
||||
} // end func
|
||||
}
|
||||
|
||||
extension DataSourceFacade {
|
||||
static func responseToShowHideReblogAction(
|
||||
dependency: NeedsDependency & AuthContextProvider,
|
||||
user: ManagedObjectRecord<MastodonUser>
|
||||
) async throws {
|
||||
_ = try await dependency.context.apiService.toggleShowReblogs(
|
||||
for: user,
|
||||
authenticationBox: dependency.authContext.mastodonAuthenticationBox)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MastodonUser>? = try? await managedObjectContext.perform {
|
||||
guard let user = menuContext.author?.object(in: managedObjectContext) else { return nil }
|
||||
return ManagedObjectRecord<MastodonUser>(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 {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -376,13 +376,22 @@ extension ProfileViewController {
|
|||
}
|
||||
let name = user.displayNameWithFallback
|
||||
let _ = ManagedObjectRecord<MastodonUser>(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<MastodonUser>(objectID: user.objectID)
|
||||
let record = ManagedObjectRecord<MastodonUser>(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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ extension SidebarViewModel {
|
|||
cell.setNeedsUpdateConfiguration()
|
||||
cell.isAccessibilityElement = true
|
||||
cell.accessibilityLabel = item.title
|
||||
cell.accessibilityTraits.insert(.button)
|
||||
|
||||
self.$currentTab
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
@ -156,6 +157,7 @@ extension SidebarViewModel {
|
|||
cell.setNeedsUpdateConfiguration()
|
||||
cell.isAccessibilityElement = true
|
||||
cell.accessibilityLabel = item.title
|
||||
cell.accessibilityTraits.insert(.button)
|
||||
}
|
||||
|
||||
// header
|
||||
|
|
|
@ -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"),
|
||||
]
|
||||
),
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>CoreData 3.xcdatamodel</string>
|
||||
<string>CoreData 4.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21279" systemVersion="21G115" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Application" representedClassName="CoreDataStack.Application" syncable="YES">
|
||||
<attribute name="identifier" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="name" attributeType="String"/>
|
||||
<attribute name="vapidKey" optional="YES" attributeType="String"/>
|
||||
<attribute name="website" optional="YES" attributeType="String"/>
|
||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="application" inverseEntity="Status"/>
|
||||
</entity>
|
||||
<entity name="DomainBlock" representedClassName="CoreDataStack.DomainBlock" syncable="YES">
|
||||
<attribute name="blockedDomain" attributeType="String"/>
|
||||
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="userID" attributeType="String"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="userID"/>
|
||||
<constraint value="domain"/>
|
||||
<constraint value="blockedDomain"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="Emoji" representedClassName="CoreDataStack.Emoji" syncable="YES">
|
||||
<attribute name="category" optional="YES" attributeType="String"/>
|
||||
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="shortcode" attributeType="String"/>
|
||||
<attribute name="staticURL" attributeType="String"/>
|
||||
<attribute name="url" attributeType="String"/>
|
||||
<attribute name="visibleInPicker" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
</entity>
|
||||
<entity name="Feed" representedClassName="CoreDataStack.Feed" syncable="YES">
|
||||
<attribute name="acctRaw" optional="YES" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="hasMore" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="isLoadingMore" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="kindRaw" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="notification" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Notification" inverseName="feeds" inverseEntity="Notification"/>
|
||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="feeds" inverseEntity="Status"/>
|
||||
</entity>
|
||||
<entity name="Instance" representedClassName="CoreDataStack.Instance" syncable="YES">
|
||||
<attribute name="configurationRaw" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="authentications" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonAuthentication" inverseName="instance" inverseEntity="MastodonAuthentication"/>
|
||||
</entity>
|
||||
<entity name="MastodonAuthentication" representedClassName="CoreDataStack.MastodonAuthentication" syncable="YES">
|
||||
<attribute name="activedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="appAccessToken" attributeType="String"/>
|
||||
<attribute name="clientID" attributeType="String"/>
|
||||
<attribute name="clientSecret" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userAccessToken" attributeType="String"/>
|
||||
<attribute name="userID" attributeType="String"/>
|
||||
<attribute name="username" attributeType="String"/>
|
||||
<relationship name="instance" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Instance" inverseName="authentications" inverseEntity="Instance"/>
|
||||
<relationship name="user" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="mastodonAuthentication" inverseEntity="MastodonUser"/>
|
||||
</entity>
|
||||
<entity name="MastodonUser" representedClassName="CoreDataStack.MastodonUser" syncable="YES">
|
||||
<attribute name="acct" attributeType="String"/>
|
||||
<attribute name="avatar" attributeType="String"/>
|
||||
<attribute name="avatarStatic" optional="YES" attributeType="String"/>
|
||||
<attribute name="bot" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="displayName" attributeType="String"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="emojis" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="fields" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="followersCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="followingCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="header" attributeType="String"/>
|
||||
<attribute name="headerStatic" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="String"/>
|
||||
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="note" optional="YES" attributeType="String"/>
|
||||
<attribute name="statusesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="suspended" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="url" optional="YES" attributeType="String"/>
|
||||
<attribute name="username" attributeType="String"/>
|
||||
<relationship name="blocking" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="blockingBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="blockingBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="blocking" inverseEntity="MastodonUser"/>
|
||||
<relationship name="bookmarked" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="bookmarkedBy" inverseEntity="Status"/>
|
||||
<relationship name="domainBlocking" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="domainBlockingBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="domainBlockingBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="domainBlocking" inverseEntity="MastodonUser"/>
|
||||
<relationship name="endorsed" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="endorsedBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="endorsedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="endorsed" inverseEntity="MastodonUser"/>
|
||||
<relationship name="favourite" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="favouritedBy" inverseEntity="Status"/>
|
||||
<relationship name="following" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="followingBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="followingBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="following" inverseEntity="MastodonUser"/>
|
||||
<relationship name="followRequested" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="followRequestedBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="followRequestedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="followRequested" inverseEntity="MastodonUser"/>
|
||||
<relationship name="mastodonAuthentication" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonAuthentication" inverseName="user" inverseEntity="MastodonAuthentication"/>
|
||||
<relationship name="muted" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="mutedBy" inverseEntity="Status"/>
|
||||
<relationship name="muting" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="mutingBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="mutingBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="muting" inverseEntity="MastodonUser"/>
|
||||
<relationship name="notifications" toMany="YES" deletionRule="Nullify" destinationEntity="Notification" inverseName="account" inverseEntity="Notification"/>
|
||||
<relationship name="pinnedStatus" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="pinnedBy" inverseEntity="Status"/>
|
||||
<relationship name="privateNotes" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PrivateNote" inverseName="to" inverseEntity="PrivateNote"/>
|
||||
<relationship name="privateNotesTo" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PrivateNote" inverseName="from" inverseEntity="PrivateNote"/>
|
||||
<relationship name="reblogged" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="rebloggedBy" inverseEntity="Status"/>
|
||||
<relationship name="searchHistories" toMany="YES" deletionRule="Nullify" destinationEntity="SearchHistory" inverseName="account" inverseEntity="SearchHistory"/>
|
||||
<relationship name="showingReblogs" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="showingReblogsBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="showingReblogsBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="showingReblogs" inverseEntity="MastodonUser"/>
|
||||
<relationship name="statuses" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="author" inverseEntity="Status"/>
|
||||
<relationship name="votePollOptions" toMany="YES" deletionRule="Nullify" destinationEntity="PollOption" inverseName="votedBy" inverseEntity="PollOption"/>
|
||||
<relationship name="votePolls" toMany="YES" deletionRule="Nullify" destinationEntity="Poll" inverseName="votedBy" inverseEntity="Poll"/>
|
||||
</entity>
|
||||
<entity name="Notification" representedClassName="CoreDataStack.Notification" syncable="YES">
|
||||
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="followRequestState" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="transientFollowRequestState" optional="YES" transient="YES" attributeType="Binary"/>
|
||||
<attribute name="typeRaw" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userID" attributeType="String"/>
|
||||
<relationship name="account" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="notifications" inverseEntity="MastodonUser"/>
|
||||
<relationship name="feeds" toMany="YES" deletionRule="Cascade" destinationEntity="Feed" inverseName="notification" inverseEntity="Feed"/>
|
||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="notifications" inverseEntity="Status"/>
|
||||
</entity>
|
||||
<entity name="Poll" representedClassName="CoreDataStack.Poll" syncable="YES">
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="expired" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="expiresAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="isVoting" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="multiple" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="votersCount" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="votesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="options" toMany="YES" deletionRule="Cascade" destinationEntity="PollOption" inverseName="poll" inverseEntity="PollOption"/>
|
||||
<relationship name="status" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="poll" inverseEntity="Status"/>
|
||||
<relationship name="votedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="votePolls" inverseEntity="MastodonUser"/>
|
||||
</entity>
|
||||
<entity name="PollOption" representedClassName="CoreDataStack.PollOption" syncable="YES">
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="index" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isSelected" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="title" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="votesCount" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="poll" maxCount="1" deletionRule="Nullify" destinationEntity="Poll" inverseName="options" inverseEntity="Poll"/>
|
||||
<relationship name="votedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="votePollOptions" inverseEntity="MastodonUser"/>
|
||||
</entity>
|
||||
<entity name="PrivateNote" representedClassName="CoreDataStack.PrivateNote" syncable="YES">
|
||||
<attribute name="note" optional="YES" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="from" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="privateNotesTo" inverseEntity="MastodonUser"/>
|
||||
<relationship name="to" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="privateNotes" inverseEntity="MastodonUser"/>
|
||||
</entity>
|
||||
<entity name="SearchHistory" representedClassName="CoreDataStack.SearchHistory" syncable="YES">
|
||||
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userID" attributeType="String" defaultValueString=""/>
|
||||
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="searchHistories" inverseEntity="MastodonUser"/>
|
||||
<relationship name="hashtag" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Tag" inverseName="searchHistories" inverseEntity="Tag"/>
|
||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="searchHistories" inverseEntity="Status"/>
|
||||
</entity>
|
||||
<entity name="Setting" representedClassName="CoreDataStack.Setting" syncable="YES">
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="preferredStaticAvatar" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="preferredStaticEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="preferredTrueBlackDarkMode" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="preferredUsingDefaultBrowser" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userID" attributeType="String"/>
|
||||
<relationship name="subscriptions" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Subscription" inverseName="setting" inverseEntity="Subscription"/>
|
||||
</entity>
|
||||
<entity name="Status" representedClassName="CoreDataStack.Status" syncable="YES">
|
||||
<attribute name="attachments" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="content" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="deletedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="emojis" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="favouritesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="String"/>
|
||||
<attribute name="inReplyToAccountID" optional="YES" attributeType="String"/>
|
||||
<attribute name="inReplyToID" optional="YES" attributeType="String"/>
|
||||
<attribute name="isSensitiveToggled" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="language" optional="YES" attributeType="String"/>
|
||||
<attribute name="mentions" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="reblogsCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="repliesCount" optional="YES" attributeType="Integer 64" usesScalarValueType="NO"/>
|
||||
<attribute name="revealedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="spoilerText" optional="YES" attributeType="String"/>
|
||||
<attribute name="text" optional="YES" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uri" attributeType="String"/>
|
||||
<attribute name="url" optional="YES" attributeType="String"/>
|
||||
<attribute name="visibilityRaw" optional="YES" attributeType="String" elementID="visibility"/>
|
||||
<relationship name="application" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Application" inverseName="status" inverseEntity="Application"/>
|
||||
<relationship name="author" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="statuses" inverseEntity="MastodonUser"/>
|
||||
<relationship name="bookmarkedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="bookmarked" inverseEntity="MastodonUser"/>
|
||||
<relationship name="favouritedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="favourite" inverseEntity="MastodonUser"/>
|
||||
<relationship name="feeds" toMany="YES" deletionRule="Cascade" destinationEntity="Feed" inverseName="status" inverseEntity="Feed"/>
|
||||
<relationship name="mutedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="muted" inverseEntity="MastodonUser"/>
|
||||
<relationship name="notifications" toMany="YES" deletionRule="Cascade" destinationEntity="Notification" inverseName="status" inverseEntity="Notification"/>
|
||||
<relationship name="pinnedBy" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="pinnedStatus" inverseEntity="MastodonUser"/>
|
||||
<relationship name="poll" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Poll" inverseName="status" inverseEntity="Poll"/>
|
||||
<relationship name="reblog" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="reblogFrom" inverseEntity="Status"/>
|
||||
<relationship name="reblogFrom" toMany="YES" deletionRule="Cascade" destinationEntity="Status" inverseName="reblog" inverseEntity="Status"/>
|
||||
<relationship name="rebloggedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="reblogged" inverseEntity="MastodonUser"/>
|
||||
<relationship name="replyFrom" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="replyTo" inverseEntity="Status"/>
|
||||
<relationship name="replyTo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="replyFrom" inverseEntity="Status"/>
|
||||
<relationship name="searchHistories" toMany="YES" deletionRule="Cascade" destinationEntity="SearchHistory" inverseName="status" inverseEntity="SearchHistory"/>
|
||||
</entity>
|
||||
<entity name="Subscription" representedClassName="CoreDataStack.Subscription" syncable="YES">
|
||||
<attribute name="activedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="endpoint" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" optional="YES" attributeType="String"/>
|
||||
<attribute name="policyRaw" attributeType="String"/>
|
||||
<attribute name="serverKey" optional="YES" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userToken" optional="YES" attributeType="String"/>
|
||||
<relationship name="alert" maxCount="1" deletionRule="Cascade" destinationEntity="SubscriptionAlerts" inverseName="subscription" inverseEntity="SubscriptionAlerts"/>
|
||||
<relationship name="setting" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Setting" inverseName="subscriptions" inverseEntity="Setting"/>
|
||||
</entity>
|
||||
<entity name="SubscriptionAlerts" representedClassName="CoreDataStack.SubscriptionAlerts" syncable="YES">
|
||||
<attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="favouriteRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="followRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="followRequestRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="mentionRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="pollRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="reblogRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="subscription" maxCount="1" deletionRule="Nullify" destinationEntity="Subscription" inverseName="alert" inverseEntity="Subscription"/>
|
||||
</entity>
|
||||
<entity name="Tag" representedClassName="CoreDataStack.Tag" syncable="YES">
|
||||
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="histories" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="name" attributeType="String"/>
|
||||
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="url" attributeType="String"/>
|
||||
<relationship name="searchHistories" toMany="YES" deletionRule="Nullify" destinationEntity="SearchHistory" inverseName="hashtag" inverseEntity="SearchHistory"/>
|
||||
</entity>
|
||||
</model>
|
|
@ -89,7 +89,8 @@ final public class MastodonUser: NSManagedObject {
|
|||
@NSManaged public private(set) var endorsedBy: Set<MastodonUser>
|
||||
@NSManaged public private(set) var domainBlocking: Set<MastodonUser>
|
||||
@NSManaged public private(set) var domainBlockingBy: Set<MastodonUser>
|
||||
|
||||
@NSManaged public private(set) var showingReblogs: Set<MastodonUser>
|
||||
@NSManaged public private(set) var showingReblogsBy: Set<MastodonUser>
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MastodonUser>,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
|
||||
|
||||
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<Mastodon.Response.Content<Mastodon.Entity.Relationship>, 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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue