mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-23 00:40:59 +00:00
Add feature to block or mute user directly from post (#1460)
* Make status context menu button frame tap target larger This makes it much easier to hit on the first try, and doesn't appear to negatively impact the layout. * Add feature to block or mute user directly from post To avoid calling the /accounts/relationships endpoint for every single status displayed, the data is only loaded when the menu is activated. When the API call comes back, the items are added to the menu (updating the view model appears to cause the menu to update, even while it is displayed) Borrowed blocking & muting logic/menu items from AccountDetailContextMenu.
This commit is contained in:
parent
8c97c9e1be
commit
194e3aea74
5 changed files with 92 additions and 1 deletions
|
@ -9,6 +9,7 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
|||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@ObservedObject private var fetcher: Fetcher
|
||||
// Whether this status is on a remote local timeline (many actions are unavailable if so)
|
||||
private let isRemote: Bool
|
||||
private let routerPath: RouterPath
|
||||
private let client: Client
|
||||
|
|
|
@ -99,6 +99,11 @@ public struct StatusRowView: View {
|
|||
}
|
||||
.contextMenu {
|
||||
contextMenu
|
||||
.onAppear {
|
||||
Task {
|
||||
await viewModel.loadAuthorRelationship()
|
||||
}
|
||||
}
|
||||
}
|
||||
.swipeActions(edge: .trailing) {
|
||||
// The actions associated with the swipes are exposed as custom accessibility actions and there is no way to remove them.
|
||||
|
|
|
@ -10,6 +10,7 @@ import SwiftUI
|
|||
public class StatusRowViewModel: ObservableObject {
|
||||
let status: Status
|
||||
let isFocused: Bool
|
||||
// Whether this status is on a remote local timeline (many actions are unavailable if so)
|
||||
let isRemote: Bool
|
||||
let showActions: Bool
|
||||
let textDisabled: Bool
|
||||
|
@ -32,6 +33,17 @@ public class StatusRowViewModel: ObservableObject {
|
|||
@Published var isLoadingRemoteContent: Bool = false
|
||||
@Published var localStatusId: String?
|
||||
@Published var localStatus: Status?
|
||||
|
||||
// The relationship our user has to the author of this post, if available
|
||||
@Published var authorRelationship: Relationship? {
|
||||
didSet {
|
||||
// if we are newly blocking or muting the author, force collapse post so it goes away
|
||||
if let relationship = authorRelationship,
|
||||
relationship.blocking || relationship.muting {
|
||||
lineLimit = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// used by the button to expand a collapsed post
|
||||
@Published var isCollapsed: Bool = true {
|
||||
|
@ -182,6 +194,11 @@ public class StatusRowViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func loadAuthorRelationship() async {
|
||||
let relationships: [Relationship]? = try? await client.get(endpoint: Accounts.relationships(ids: [status.reblog?.account.id ?? status.account.id]))
|
||||
authorRelationship = relationships?.first
|
||||
}
|
||||
|
||||
private func embededStatusURL() -> URL? {
|
||||
let content = finalStatus.content
|
||||
if !content.statusesURLs.isEmpty,
|
||||
|
|
|
@ -7,6 +7,7 @@ import SwiftUI
|
|||
struct StatusRowContextMenu: View {
|
||||
@Environment(\.displayScale) var displayScale
|
||||
|
||||
@EnvironmentObject private var client: Client
|
||||
@EnvironmentObject private var sceneDelegate: SceneDelegate
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var account: CurrentAccount
|
||||
|
@ -186,6 +187,68 @@ struct StatusRowContextMenu: View {
|
|||
} label: {
|
||||
Label("status.action.message", systemImage: "tray.full")
|
||||
}
|
||||
|
||||
|
||||
if viewModel.authorRelationship?.blocking == true {
|
||||
Button {
|
||||
Task {
|
||||
do {
|
||||
let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account
|
||||
viewModel.authorRelationship = try await client.post(endpoint: Accounts.unblock(id: operationAccount.id))
|
||||
} catch {
|
||||
print("Error while unblocking: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label("account.action.unblock", systemImage: "person.crop.circle.badge.exclamationmark")
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
Task {
|
||||
do {
|
||||
let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account
|
||||
viewModel.authorRelationship = try await client.post(endpoint: Accounts.block(id: operationAccount.id))
|
||||
} catch {
|
||||
print("Error while blocking: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label("account.action.block", systemImage: "person.crop.circle.badge.xmark")
|
||||
}
|
||||
}
|
||||
|
||||
if viewModel.authorRelationship?.muting == true {
|
||||
Button {
|
||||
Task {
|
||||
do {
|
||||
let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account
|
||||
viewModel.authorRelationship = try await client.post(endpoint: Accounts.unmute(id: operationAccount.id))
|
||||
} catch {
|
||||
print("Error while unmuting: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label("account.action.unmute", systemImage: "speaker")
|
||||
}
|
||||
} else {
|
||||
Menu {
|
||||
ForEach(Duration.mutingDurations(), id: \.rawValue) { duration in
|
||||
Button(duration.description) {
|
||||
Task {
|
||||
do {
|
||||
let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account
|
||||
viewModel.authorRelationship = try await client.post(endpoint: Accounts.mute(id: operationAccount.id, json: MuteData(duration: duration.rawValue)))
|
||||
} catch {
|
||||
print("Error while muting: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label("account.action.mute", systemImage: "speaker.slash")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Section {
|
||||
|
|
|
@ -113,9 +113,14 @@ struct StatusRowHeaderView: View {
|
|||
private var contextMenuButton: some View {
|
||||
Menu {
|
||||
StatusRowContextMenu(viewModel: viewModel)
|
||||
.onAppear {
|
||||
Task {
|
||||
await viewModel.loadAuthorRelationship()
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "ellipsis")
|
||||
.frame(width: 20, height: 20)
|
||||
.frame(width: 40, height: 40)
|
||||
}
|
||||
.menuStyle(.borderlessButton)
|
||||
.foregroundColor(.gray)
|
||||
|
|
Loading…
Reference in a new issue