Compare commits
2 Commits
9d1c3f1410
...
5c479e3bf0
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 5c479e3bf0 | |
Shadowfacts | 0413f326a0 |
|
@ -112,12 +112,12 @@ public final class Account: AccountProtocol, Decodable {
|
||||||
return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(accountID)/unfollow")
|
return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(accountID)/unfollow")
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func block(_ account: Account) -> Request<Relationship> {
|
public static func block(_ accountID: String) -> Request<Relationship> {
|
||||||
return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(account.id)/block")
|
return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(accountID)/block")
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func unblock(_ account: Account) -> Request<Relationship> {
|
public static func unblock(_ accountID: String) -> Request<Relationship> {
|
||||||
return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(account.id)/unblock")
|
return Request<Relationship>(method: .post, path: "/api/v1/accounts/\(accountID)/unblock")
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func mute(_ account: Account, notifications: Bool? = nil) -> Request<Relationship> {
|
public static func mute(_ account: Account, notifications: Bool? = nil) -> Request<Relationship> {
|
||||||
|
|
|
@ -67,15 +67,24 @@ enum CompositionAttachmentData {
|
||||||
completion(.failure(.missingData))
|
completion(.failure(.missingData))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let utType: UTType
|
let utType: UTType
|
||||||
if dataUTI == "public.heic" {
|
let image = CIImage(data: data)!
|
||||||
// neither Mastodon nor Pleroma handles HEIC well, so convert to JPEG
|
let needsColorSpaceConversion = image.colorSpace?.name != CGColorSpace.sRGB
|
||||||
let image = CIImage(data: data)!
|
|
||||||
|
// neither Mastodon nor Pleroma handles HEIC well, so convert to JPEG
|
||||||
|
// they also do a bad job converting wide color gamut images (they seem to just drop the profile, letting the wide-gamut values be reinterprete as sRGB)
|
||||||
|
// if that changes in the future, we'll need to pass the InstanceFeatures in here somehow and gate the conversion
|
||||||
|
if needsColorSpaceConversion || dataUTI == "public.heic" {
|
||||||
let context = CIContext()
|
let context = CIContext()
|
||||||
let colorSpace = image.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!
|
let sRGB = CGColorSpace(name: CGColorSpace.sRGB)!
|
||||||
data = context.jpegRepresentation(of: image, colorSpace: colorSpace, options: [:])!
|
if dataUTI == "public.png" {
|
||||||
utType = .jpeg
|
data = context.pngRepresentation(of: image, format: .ARGB8, colorSpace: sRGB)!
|
||||||
|
utType = .png
|
||||||
|
} else {
|
||||||
|
data = context.jpegRepresentation(of: image, colorSpace: sRGB)!
|
||||||
|
utType = .jpeg
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
utType = UTType(dataUTI)!
|
utType = UTType(dataUTI)!
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,46 +42,6 @@ extension MenuActionProvider {
|
||||||
guard let mastodonController = mastodonController,
|
guard let mastodonController = mastodonController,
|
||||||
let account = mastodonController.persistentContainer.account(for: accountID) else { return [] }
|
let account = mastodonController.persistentContainer.account(for: accountID) else { return [] }
|
||||||
|
|
||||||
guard let loggedInAccountID = mastodonController.accountInfo?.id else {
|
|
||||||
// user is logged out
|
|
||||||
return [
|
|
||||||
openInSafariAction(url: account.url),
|
|
||||||
createAction(identifier: "share", title: "Share", systemImageName: "square.and.arrow.up", handler: { [weak self, weak sourceView] (_) in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.navigationDelegate?.showMoreOptions(forAccount: accountID, sourceView: sourceView)
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
let actionsSection: [UIMenuElement] = [
|
|
||||||
createAction(identifier: "sendmessage", title: "Send Message", systemImageName: "envelope", handler: { [weak self] (_) in
|
|
||||||
guard let self = self else { return }
|
|
||||||
let draft = self.mastodonController!.createDraft(mentioningAcct: account.acct)
|
|
||||||
draft.visibility = .direct
|
|
||||||
self.navigationDelegate?.compose(editing: draft)
|
|
||||||
}),
|
|
||||||
UIDeferredMenuElement.uncached({ @MainActor [unowned self] elementHandler in
|
|
||||||
let relationship = Task {
|
|
||||||
await fetchRelationship(accountID: accountID, mastodonController: mastodonController)
|
|
||||||
}
|
|
||||||
// workaround for #198, may result in showing outdated relationship, so only do so where necessary
|
|
||||||
if ProcessInfo.processInfo.isiOSAppOnMac,
|
|
||||||
let mo = mastodonController.persistentContainer.relationship(forAccount: accountID),
|
|
||||||
let action = self.followAction(for: mo, mastodonController: mastodonController) {
|
|
||||||
elementHandler([action])
|
|
||||||
} else {
|
|
||||||
Task { @MainActor in
|
|
||||||
if let relationship = await relationship.value,
|
|
||||||
let action = self.followAction(for: relationship, mastodonController: mastodonController) {
|
|
||||||
elementHandler([action])
|
|
||||||
} else {
|
|
||||||
elementHandler([])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
|
|
||||||
var shareSection = [
|
var shareSection = [
|
||||||
openInSafariAction(url: account.url),
|
openInSafariAction(url: account.url),
|
||||||
createAction(identifier: "share", title: "Share", systemImageName: "square.and.arrow.up", handler: { [weak self, weak sourceView] (_) in
|
createAction(identifier: "share", title: "Share", systemImageName: "square.and.arrow.up", handler: { [weak self, weak sourceView] (_) in
|
||||||
|
@ -90,11 +50,32 @@ extension MenuActionProvider {
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
|
guard let loggedInAccountID = mastodonController.accountInfo?.id else {
|
||||||
|
// user is logged out
|
||||||
|
return shareSection
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionsSection: [UIMenuElement] = [
|
||||||
|
createAction(identifier: "sendmessage", title: "Send Message", systemImageName: "envelope", handler: { [weak self] (_) in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let draft = self.mastodonController!.createDraft(mentioningAcct: account.acct)
|
||||||
|
draft.visibility = .direct
|
||||||
|
self.navigationDelegate?.compose(editing: draft)
|
||||||
|
})
|
||||||
|
]
|
||||||
|
var suppressSection: [UIMenuElement] = []
|
||||||
|
|
||||||
|
if accountID != loggedInAccountID {
|
||||||
|
actionsSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.followAction(for: $0, mastodonController: $1) }))
|
||||||
|
suppressSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.blockAction(for: $0, mastodonController: $1) }))
|
||||||
|
}
|
||||||
|
|
||||||
addOpenInNewWindow(actions: &shareSection, activity: UserActivityManager.showProfileActivity(id: accountID, accountID: loggedInAccountID))
|
addOpenInNewWindow(actions: &shareSection, activity: UserActivityManager.showProfileActivity(id: accountID, accountID: loggedInAccountID))
|
||||||
|
|
||||||
return [
|
return [
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection),
|
UIMenu(options: .displayInline, children: shareSection),
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: actionsSection),
|
UIMenu(options: .displayInline, children: actionsSection),
|
||||||
|
UIMenu(options: .displayInline, children: suppressSection),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,12 +348,29 @@ extension MenuActionProvider {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func relationshipAction(accountID: String, mastodonController: MastodonController, builder: @escaping @MainActor (RelationshipMO, MastodonController) -> UIMenuElement) -> UIDeferredMenuElement {
|
||||||
|
return UIDeferredMenuElement.uncached({ @MainActor elementHandler in
|
||||||
|
let relationship = Task {
|
||||||
|
await fetchRelationship(accountID: accountID, mastodonController: mastodonController)
|
||||||
|
}
|
||||||
|
// workaround for #198, may result in showing outdated relationship, so only do so where necessary
|
||||||
|
if ProcessInfo.processInfo.isiOSAppOnMac,
|
||||||
|
let mo = mastodonController.persistentContainer.relationship(forAccount: accountID) {
|
||||||
|
elementHandler([builder(mo, mastodonController)])
|
||||||
|
} else {
|
||||||
|
Task { @MainActor in
|
||||||
|
if let relationship = await relationship.value {
|
||||||
|
elementHandler([builder(relationship, mastodonController)])
|
||||||
|
} else {
|
||||||
|
elementHandler([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func followAction(for relationship: RelationshipMO, mastodonController: MastodonController) -> UIMenuElement? {
|
private func followAction(for relationship: RelationshipMO, mastodonController: MastodonController) -> UIMenuElement {
|
||||||
guard let ownAccount = mastodonController.account,
|
|
||||||
relationship.accountID != ownAccount.id else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
let accountID = relationship.accountID
|
let accountID = relationship.accountID
|
||||||
let following = relationship.following
|
let following = relationship.following
|
||||||
return createAction(identifier: "follow", title: following ? "Unfollow" : "Follow", systemImageName: following ? "person.badge.minus" : "person.badge.plus") { _ in
|
return createAction(identifier: "follow", title: following ? "Unfollow" : "Follow", systemImageName: following ? "person.badge.minus" : "person.badge.plus") { _ in
|
||||||
|
@ -393,6 +391,36 @@ extension MenuActionProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
private func blockAction(for relationship: RelationshipMO, mastodonController: MastodonController) -> UIMenuElement {
|
||||||
|
let accountID = relationship.accountID
|
||||||
|
let blocking = relationship.blocking
|
||||||
|
let handler = { (_: UIAction) in
|
||||||
|
let request = (blocking ? Account.unblock : Account.block)(accountID)
|
||||||
|
mastodonController.run(request) { response in
|
||||||
|
switch response {
|
||||||
|
case .failure(let error):
|
||||||
|
if let toastable = self.toastableViewController {
|
||||||
|
let config = ToastConfiguration(from: error, with: "Error \(blocking ? "Unb" : "B")locking", in: toastable, retryAction: nil)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
toastable.showToast(configuration: config, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .success(let relationship, _):
|
||||||
|
mastodonController.persistentContainer.addOrUpdate(relationship: relationship)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if blocking {
|
||||||
|
return createAction(identifier: "block", title: "Unblock", systemImageName: "circle.slash", handler: handler)
|
||||||
|
} else {
|
||||||
|
return UIMenu(title: "Block", image: UIImage(systemName: "circle.slash"), children: [
|
||||||
|
UIAction(title: "Cancel", handler: { _ in }),
|
||||||
|
UIAction(title: "Block \(relationship.account!.displayOrUserName)", image: UIImage(systemName: "circle.slash"), attributes: .destructive, handler: handler),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchRelationship(accountID: String, mastodonController: MastodonController) async -> RelationshipMO? {
|
private func fetchRelationship(accountID: String, mastodonController: MastodonController) async -> RelationshipMO? {
|
||||||
|
|
Loading…
Reference in New Issue