Merge pull request #505 from mastodon/365-hide-reblog
Show/Hide Reblogs (#365)
This commit is contained in:
commit
70e669c361
|
@ -100,6 +100,7 @@ GEM
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
arm64-darwin-21
|
arm64-darwin-21
|
||||||
|
x86_64-darwin-21
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
arkana
|
arkana
|
||||||
|
|
|
@ -180,7 +180,9 @@
|
||||||
"unmute": "Unmute",
|
"unmute": "Unmute",
|
||||||
"unmute_user": "Unmute %s",
|
"unmute_user": "Unmute %s",
|
||||||
"muted": "Muted",
|
"muted": "Muted",
|
||||||
"edit_info": "Edit Info"
|
"edit_info": "Edit Info",
|
||||||
|
"show_reblogs": "Show Reblogs",
|
||||||
|
"hide_reblogs": "Hide Reblogs",
|
||||||
},
|
},
|
||||||
"timeline": {
|
"timeline": {
|
||||||
"filtered": "Filtered",
|
"filtered": "Filtered",
|
||||||
|
@ -455,7 +457,15 @@
|
||||||
"confirm_unblock_user": {
|
"confirm_unblock_user": {
|
||||||
"title": "Unblock Account",
|
"title": "Unblock Account",
|
||||||
"message": "Confirm to unblock %s"
|
"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": {
|
"accessibility": {
|
||||||
"show_avatar_image": "Show avatar image",
|
"show_avatar_image": "Show avatar image",
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/Alamofire/Alamofire.git",
|
"location" : "https://github.com/Alamofire/Alamofire.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "354dda32d89fc8cd4f5c46487f64957d355f53d8",
|
"revision" : "8dd85aee02e39dd280c75eef88ffdb86eed4b07b",
|
||||||
"version" : "5.6.1"
|
"version" : "5.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -41,8 +41,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/Flipboard/FLAnimatedImage.git",
|
"location" : "https://github.com/Flipboard/FLAnimatedImage.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "e7f9fd4681ae41bf6f3056db08af4f401d61da52",
|
"revision" : "d4f07b6f164d53c1212c3e54d6460738b1981e9f",
|
||||||
"version" : "1.0.16"
|
"version" : "1.0.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -95,8 +95,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/kean/Nuke.git",
|
"location" : "https://github.com/kean/Nuke.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "0ea7545b5c918285aacc044dc75048625c8257cc",
|
"revision" : "a002b7fd786f2df2ed4333fe73a9727499fd9d97",
|
||||||
"version" : "10.8.0"
|
"version" : "10.11.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -113,8 +113,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/uias/Pageboy",
|
"location" : "https://github.com/uias/Pageboy",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "34ecb6e7c4e0e07494960ab2f7cc9a02293915a6",
|
"revision" : "af8fa81788b893205e1ff42ddd88c5b0b315d7c5",
|
||||||
"version" : "3.6.2"
|
"version" : "3.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -131,8 +131,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/SDWebImage/SDWebImage.git",
|
"location" : "https://github.com/SDWebImage/SDWebImage.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "2e63d0061da449ad0ed130768d05dceb1496de44",
|
"revision" : "318cca556b0489aede0cd98d8d0c7f1408ab7bd6",
|
||||||
"version" : "5.12.5"
|
"version" : "5.13.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -176,8 +176,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/scinfu/SwiftSoup.git",
|
"location" : "https://github.com/scinfu/SwiftSoup.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "41e7c263fb8c277e980ebcb9b0b5f6031d3d4886",
|
"revision" : "6778575285177365cbad3e5b8a72f2a20583cfec",
|
||||||
"version" : "2.4.2"
|
"version" : "2.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -189,15 +189,6 @@
|
||||||
"version" : "0.1.4"
|
"version" : "0.1.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"identity" : "swiftyjson",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/SwiftyJSON/SwiftyJSON.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "b3dcd7dbd0d488e1a7077cb33b00f2083e382f07",
|
|
||||||
"version" : "5.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "tabbarpager",
|
"identity" : "tabbarpager",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|
|
@ -132,3 +132,14 @@ extension DataSourceFacade {
|
||||||
}
|
}
|
||||||
} // end func
|
} // 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
|
menuContext: MenuContext
|
||||||
) async throws {
|
) async throws {
|
||||||
switch action {
|
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):
|
case .muteUser(let actionContext):
|
||||||
let alertController = UIAlertController(
|
let alertController = UIAlertController(
|
||||||
title: actionContext.isMuting ? L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.title : L10n.Scene.Profile.RelationshipActionAlert.ConfirmMuteUser.title,
|
title: actionContext.isMuting ? L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.title : L10n.Scene.Profile.RelationshipActionAlert.ConfirmMuteUser.title,
|
||||||
|
@ -230,9 +269,9 @@ extension DataSourceFacade {
|
||||||
} // end Task
|
} // end Task
|
||||||
}
|
}
|
||||||
alertController.addAction(confirmAction)
|
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)
|
alertController.addAction(cancelAction)
|
||||||
dependency.present(alertController, animated: true, completion: nil)
|
dependency.present(alertController, animated: true)
|
||||||
case .blockUser(let actionContext):
|
case .blockUser(let actionContext):
|
||||||
let alertController = UIAlertController(
|
let alertController = UIAlertController(
|
||||||
title: actionContext.isBlocking ? L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.title : L10n.Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.title,
|
title: actionContext.isBlocking ? L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.title : L10n.Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.title,
|
||||||
|
@ -258,9 +297,9 @@ extension DataSourceFacade {
|
||||||
} // end Task
|
} // end Task
|
||||||
}
|
}
|
||||||
alertController.addAction(confirmAction)
|
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)
|
alertController.addAction(cancelAction)
|
||||||
dependency.present(alertController, animated: true, completion: nil)
|
dependency.present(alertController, animated: true)
|
||||||
case .reportUser:
|
case .reportUser:
|
||||||
Task {
|
Task {
|
||||||
guard let user = menuContext.author else { return }
|
guard let user = menuContext.author else { return }
|
||||||
|
@ -349,9 +388,9 @@ extension DataSourceFacade {
|
||||||
} // end Task
|
} // end Task
|
||||||
}
|
}
|
||||||
alertController.addAction(confirmAction)
|
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)
|
alertController.addAction(cancelAction)
|
||||||
dependency.present(alertController, animated: true, completion: nil)
|
dependency.present(alertController, animated: true)
|
||||||
|
|
||||||
}
|
}
|
||||||
} // end func
|
} // end func
|
||||||
|
@ -371,3 +410,4 @@ extension DataSourceFacade {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -376,13 +376,22 @@ extension ProfileViewController {
|
||||||
}
|
}
|
||||||
let name = user.displayNameWithFallback
|
let name = user.displayNameWithFallback
|
||||||
let _ = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
let _ = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
||||||
let menu = MastodonMenu.setupMenu(
|
|
||||||
actions: [
|
var menuActions: [MastodonMenu.Action] = [
|
||||||
.muteUser(.init(name: name, isMuting: self.viewModel.relationshipViewModel.isMuting)),
|
.muteUser(.init(name: name, isMuting: self.viewModel.relationshipViewModel.isMuting)),
|
||||||
.blockUser(.init(name: name, isBlocking: self.viewModel.relationshipViewModel.isBlocking)),
|
.blockUser(.init(name: name, isBlocking: self.viewModel.relationshipViewModel.isBlocking)),
|
||||||
.reportUser(.init(name: name)),
|
.reportUser(.init(name: name)),
|
||||||
.shareUser(.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: menuActions,
|
||||||
delegate: self
|
delegate: self
|
||||||
)
|
)
|
||||||
return menu
|
return menu
|
||||||
|
@ -397,8 +406,10 @@ extension ProfileViewController {
|
||||||
}
|
}
|
||||||
} receiveValue: { [weak self] menu in
|
} receiveValue: { [weak self] menu in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
OperationQueue.main.addOperation {
|
||||||
self.moreMenuBarButtonItem.menu = menu
|
self.moreMenuBarButtonItem.menu = menu
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,7 +751,7 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
|
||||||
let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.EditProfileFailure.title, preferredStyle: .alert)
|
let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.EditProfileFailure.title, preferredStyle: .alert)
|
||||||
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
|
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
|
||||||
alertController.addAction(okAction)
|
alertController.addAction(okAction)
|
||||||
self.coordinator.present(
|
_ = self.coordinator.present(
|
||||||
scene: .alertController(alertController: alertController),
|
scene: .alertController(alertController: alertController),
|
||||||
from: nil,
|
from: nil,
|
||||||
transition: .alertController(animated: true, completion: nil)
|
transition: .alertController(animated: true, completion: nil)
|
||||||
|
@ -763,11 +774,11 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
|
||||||
break
|
break
|
||||||
case .follow, .request, .pending, .following:
|
case .follow, .request, .pending, .following:
|
||||||
guard let user = viewModel.user else { return }
|
guard let user = viewModel.user else { return }
|
||||||
let reocrd = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
let record = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
||||||
Task {
|
Task {
|
||||||
try await DataSourceFacade.responseToUserFollowAction(
|
try await DataSourceFacade.responseToUserFollowAction(
|
||||||
dependency: self,
|
dependency: self,
|
||||||
user: reocrd
|
user: record
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .muting:
|
case .muting:
|
||||||
|
@ -816,10 +827,8 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
|
||||||
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
|
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
|
||||||
alertController.addAction(cancelAction)
|
alertController.addAction(cancelAction)
|
||||||
present(alertController, animated: true, completion: nil)
|
present(alertController, animated: true, completion: nil)
|
||||||
case .blocked:
|
case .blocked, .showReblogs, .isMyself,.followingBy, .blockingBy, .suspended, .edit, .editing, .updating:
|
||||||
break
|
break
|
||||||
default:
|
|
||||||
assertionFailure()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,6 @@ let package = Package(
|
||||||
.package(url: "https://github.com/MainasuK/CommonOSLog", from: "0.1.1"),
|
.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/MainasuK/FPSIndicator.git", from: "1.0.0"),
|
||||||
.package(url: "https://github.com/slackhq/PanModal.git", from: "1.2.7"),
|
.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/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/MetaTextKit.git", exact: "2.2.5"),
|
||||||
.package(url: "https://github.com/TwidereProject/TabBarPager.git", from: "0.1.0"),
|
.package(url: "https://github.com/TwidereProject/TabBarPager.git", from: "0.1.0"),
|
||||||
|
@ -103,7 +102,6 @@ let package = Package(
|
||||||
.target(
|
.target(
|
||||||
name: "MastodonSDK",
|
name: "MastodonSDK",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.product(name: "SwiftyJSON", package: "SwiftyJSON"),
|
|
||||||
.product(name: "NIOHTTP1", package: "swift-nio"),
|
.product(name: "NIOHTTP1", package: "swift-nio"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>_XCCurrentVersionName</key>
|
<key>_XCCurrentVersionName</key>
|
||||||
<string>CoreData 3.xcdatamodel</string>
|
<string>CoreData 4.xcdatamodel</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</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 endorsedBy: Set<MastodonUser>
|
||||||
@NSManaged public private(set) var domainBlocking: Set<MastodonUser>
|
@NSManaged public private(set) var domainBlocking: Set<MastodonUser>
|
||||||
@NSManaged public private(set) var domainBlockingBy: 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 {
|
extension MastodonUser {
|
||||||
|
@ -521,6 +522,7 @@ extension MastodonUser: AutoUpdatableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func update(isDomainBlocking: Bool, by mastodonUser: MastodonUser) {
|
public func update(isDomainBlocking: Bool, by mastodonUser: MastodonUser) {
|
||||||
if isDomainBlocking {
|
if isDomainBlocking {
|
||||||
if !self.domainBlockingBy.contains(mastodonUser) {
|
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)
|
user.update(isBlocking: relationship.blocking, by: me)
|
||||||
relationship.domainBlocking.flatMap { user.update(isDomainBlocking: $0, by: me) }
|
relationship.domainBlocking.flatMap { user.update(isDomainBlocking: $0, by: me) }
|
||||||
relationship.blockedBy.flatMap { me.update(isBlocking: $0, by: user) }
|
relationship.blockedBy.flatMap { me.update(isBlocking: $0, by: user) }
|
||||||
|
relationship.showingReblogs.flatMap { me.update(isShowingReblogs: $0, by: user) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,4 +122,55 @@ extension APIService {
|
||||||
return response
|
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")
|
public static let follow = L10n.tr("Localizable", "Common.Controls.Friendship.Follow")
|
||||||
/// Following
|
/// Following
|
||||||
public static let following = L10n.tr("Localizable", "Common.Controls.Friendship.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
|
/// Mute
|
||||||
public static let mute = L10n.tr("Localizable", "Common.Controls.Friendship.Mute")
|
public static let mute = L10n.tr("Localizable", "Common.Controls.Friendship.Mute")
|
||||||
/// Muted
|
/// Muted
|
||||||
|
@ -210,6 +212,8 @@ public enum L10n {
|
||||||
public static let pending = L10n.tr("Localizable", "Common.Controls.Friendship.Pending")
|
public static let pending = L10n.tr("Localizable", "Common.Controls.Friendship.Pending")
|
||||||
/// Request
|
/// Request
|
||||||
public static let request = L10n.tr("Localizable", "Common.Controls.Friendship.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
|
/// Unblock
|
||||||
public static let unblock = L10n.tr("Localizable", "Common.Controls.Friendship.Unblock")
|
public static let unblock = L10n.tr("Localizable", "Common.Controls.Friendship.Unblock")
|
||||||
/// Unblock %@
|
/// Unblock %@
|
||||||
|
@ -709,6 +713,12 @@ public enum L10n {
|
||||||
/// Block Account
|
/// Block Account
|
||||||
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.Title")
|
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 {
|
public enum ConfirmMuteUser {
|
||||||
/// Confirm to mute %@
|
/// Confirm to mute %@
|
||||||
public static func message(_ p1: Any) -> String {
|
public static func message(_ p1: Any) -> String {
|
||||||
|
@ -717,6 +727,12 @@ public enum L10n {
|
||||||
/// Mute Account
|
/// Mute Account
|
||||||
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmMuteUser.Title")
|
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 {
|
public enum ConfirmUnblockUser {
|
||||||
/// Confirm to unblock %@
|
/// Confirm to unblock %@
|
||||||
public static func message(_ p1: Any) -> String {
|
public static func message(_ p1: Any) -> String {
|
||||||
|
|
|
@ -77,6 +77,9 @@ Please check your internet connection.";
|
||||||
"Common.Controls.Friendship.UnblockUser" = "Unblock %@";
|
"Common.Controls.Friendship.UnblockUser" = "Unblock %@";
|
||||||
"Common.Controls.Friendship.Unmute" = "Unmute";
|
"Common.Controls.Friendship.Unmute" = "Unmute";
|
||||||
"Common.Controls.Friendship.UnmuteUser" = "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.ComposeNewPost" = "Compose New Post";
|
||||||
"Common.Controls.Keyboard.Common.OpenSettings" = "Open Settings";
|
"Common.Controls.Keyboard.Common.OpenSettings" = "Open Settings";
|
||||||
"Common.Controls.Keyboard.Common.ShowFavorites" = "Show Favorites";
|
"Common.Controls.Keyboard.Common.ShowFavorites" = "Show Favorites";
|
||||||
|
@ -262,6 +265,10 @@ uploaded to Mastodon.";
|
||||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.Title" = "Unblock Account";
|
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.Title" = "Unblock Account";
|
||||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Message" = "Confirm to unmute %@";
|
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Message" = "Confirm to unmute %@";
|
||||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Title" = "Unmute Account";
|
"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.About" = "About";
|
||||||
"Scene.Profile.SegmentedControl.Media" = "Media";
|
"Scene.Profile.SegmentedControl.Media" = "Media";
|
||||||
"Scene.Profile.SegmentedControl.Posts" = "Posts";
|
"Scene.Profile.SegmentedControl.Posts" = "Posts";
|
||||||
|
|
|
@ -45,11 +45,23 @@ extension MastodonMenu {
|
||||||
case reportUser(ReportUserActionContext)
|
case reportUser(ReportUserActionContext)
|
||||||
case shareUser(ShareUserActionContext)
|
case shareUser(ShareUserActionContext)
|
||||||
case bookmarkStatus(BookmarkStatusActionContext)
|
case bookmarkStatus(BookmarkStatusActionContext)
|
||||||
|
case hideReblogs(HideReblogsActionContext)
|
||||||
case shareStatus
|
case shareStatus
|
||||||
case deleteStatus
|
case deleteStatus
|
||||||
|
|
||||||
func build(delegate: MastodonMenuDelegate) -> BuiltAction {
|
func build(delegate: MastodonMenuDelegate) -> BuiltAction {
|
||||||
switch self {
|
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):
|
case .muteUser(let context):
|
||||||
let muteAction = BuiltAction(
|
let muteAction = BuiltAction(
|
||||||
title: context.isMuting ? L10n.Common.Controls.Friendship.unmuteUser(context.name) : L10n.Common.Controls.Friendship.muteUser(context.name),
|
title: context.isMuting ? L10n.Common.Controls.Friendship.unmuteUser(context.name) : L10n.Common.Controls.Friendship.muteUser(context.name),
|
||||||
|
@ -206,4 +218,11 @@ extension MastodonMenu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct HideReblogsActionContext {
|
||||||
|
public let showReblogs: Bool
|
||||||
|
|
||||||
|
public init(showReblogs: Bool) {
|
||||||
|
self.showReblogs = showReblogs
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import MastodonLocalization
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
|
|
||||||
public enum RelationshipAction: Int, CaseIterable {
|
public enum RelationshipAction: Int, CaseIterable {
|
||||||
|
case showReblogs
|
||||||
case isMyself
|
case isMyself
|
||||||
case followingBy
|
case followingBy
|
||||||
case blockingBy
|
case blockingBy
|
||||||
|
@ -57,7 +58,7 @@ public struct RelationshipActionOptionSet: OptionSet {
|
||||||
public static let edit = RelationshipAction.edit.option
|
public static let edit = RelationshipAction.edit.option
|
||||||
public static let editing = RelationshipAction.editing.option
|
public static let editing = RelationshipAction.editing.option
|
||||||
public static let updating = RelationshipAction.updating.option
|
public static let updating = RelationshipAction.updating.option
|
||||||
|
public static let showReblogs = RelationshipAction.showReblogs.option
|
||||||
public static let editOptions: RelationshipActionOptionSet = [.edit, .editing, .updating]
|
public static let editOptions: RelationshipActionOptionSet = [.edit, .editing, .updating]
|
||||||
|
|
||||||
public func highPriorityAction(except: RelationshipActionOptionSet) -> RelationshipAction? {
|
public func highPriorityAction(except: RelationshipActionOptionSet) -> RelationshipAction? {
|
||||||
|
@ -90,9 +91,9 @@ public struct RelationshipActionOptionSet: OptionSet {
|
||||||
case .edit: return L10n.Common.Controls.Friendship.editInfo
|
case .edit: return L10n.Common.Controls.Friendship.editInfo
|
||||||
case .editing: return L10n.Common.Controls.Actions.done
|
case .editing: return L10n.Common.Controls.Actions.done
|
||||||
case .updating: return " "
|
case .updating: return " "
|
||||||
|
case .showReblogs: return " "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class RelationshipViewModel {
|
public final class RelationshipViewModel {
|
||||||
|
@ -114,6 +115,7 @@ public final class RelationshipViewModel {
|
||||||
@Published public var isFollowing = false
|
@Published public var isFollowing = false
|
||||||
@Published public var isFollowingBy = false
|
@Published public var isFollowingBy = false
|
||||||
@Published public var isMuting = false
|
@Published public var isMuting = false
|
||||||
|
@Published public var showReblogs = false
|
||||||
@Published public var isBlocking = false
|
@Published public var isBlocking = false
|
||||||
@Published public var isBlockingBy = false
|
@Published public var isBlockingBy = false
|
||||||
@Published public var isSuspended = false
|
@Published public var isSuspended = false
|
||||||
|
@ -184,6 +186,7 @@ extension RelationshipViewModel {
|
||||||
self.isBlockingBy = optionSet.contains(.blockingBy)
|
self.isBlockingBy = optionSet.contains(.blockingBy)
|
||||||
self.isBlocking = optionSet.contains(.blocking)
|
self.isBlocking = optionSet.contains(.blocking)
|
||||||
self.isSuspended = optionSet.contains(.suspended)
|
self.isSuspended = optionSet.contains(.suspended)
|
||||||
|
self.showReblogs = optionSet.contains(.showReblogs)
|
||||||
|
|
||||||
self.optionSet = optionSet
|
self.optionSet = optionSet
|
||||||
}
|
}
|
||||||
|
@ -196,6 +199,7 @@ extension RelationshipViewModel {
|
||||||
isBlockingBy = false
|
isBlockingBy = false
|
||||||
isBlocking = false
|
isBlocking = false
|
||||||
optionSet = nil
|
optionSet = nil
|
||||||
|
showReblogs = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +218,7 @@ extension RelationshipViewModel {
|
||||||
let isMuting = user.mutingBy.contains(me)
|
let isMuting = user.mutingBy.contains(me)
|
||||||
let isBlockingBy = me.blockingBy.contains(user)
|
let isBlockingBy = me.blockingBy.contains(user)
|
||||||
let isBlocking = user.blockingBy.contains(me)
|
let isBlocking = user.blockingBy.contains(me)
|
||||||
|
let isShowingReblogs = me.showingReblogsBy.contains(user)
|
||||||
|
|
||||||
var optionSet: RelationshipActionOptionSet = [.follow]
|
var optionSet: RelationshipActionOptionSet = [.follow]
|
||||||
|
|
||||||
|
@ -253,6 +258,10 @@ extension RelationshipViewModel {
|
||||||
optionSet.insert(.suspended)
|
optionSet.insert(.suspended)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isShowingReblogs {
|
||||||
|
optionSet.insert(.showReblogs)
|
||||||
|
}
|
||||||
|
|
||||||
return optionSet
|
return optionSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue