mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-28 19:21:16 +00:00
Batch accept / reject notifications requests
This commit is contained in:
parent
d8f2b0f3ae
commit
15a6fd6f1c
3 changed files with 159 additions and 52 deletions
|
@ -11,8 +11,8 @@ public enum Notifications: Endpoint {
|
|||
case policy
|
||||
case putPolicy(policy: Models.NotificationsPolicy)
|
||||
case requests
|
||||
case acceptRequest(id: String)
|
||||
case dismissRequest(id: String)
|
||||
case acceptRequests(ids: [String])
|
||||
case dismissRequests(ids: [String])
|
||||
case clear
|
||||
|
||||
public func path() -> String {
|
||||
|
@ -25,10 +25,10 @@ public enum Notifications: Endpoint {
|
|||
"notifications/policy"
|
||||
case .requests:
|
||||
"notifications/requests"
|
||||
case let .acceptRequest(id):
|
||||
"notifications/requests/\(id)/accept"
|
||||
case let .dismissRequest(id):
|
||||
"notifications/requests/\(id)/dismiss"
|
||||
case .acceptRequests:
|
||||
"notifications/requests/accept"
|
||||
case .dismissRequests:
|
||||
"notifications/requests/dismiss"
|
||||
case .clear:
|
||||
"notifications/clear"
|
||||
}
|
||||
|
@ -58,6 +58,8 @@ public enum Notifications: Endpoint {
|
|||
}
|
||||
}
|
||||
return params
|
||||
case let .acceptRequests(ids), let .dismissRequests(ids):
|
||||
return ids.map { URLQueryItem(name: "id[]", value: $0) }
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
@ -7,6 +8,7 @@ import SwiftUI
|
|||
public struct NotificationsRequestsListView: View {
|
||||
@Environment(Client.self) private var client
|
||||
@Environment(Theme.self) private var theme
|
||||
@Environment(RouterPath.self) private var routerPath
|
||||
|
||||
enum ViewState {
|
||||
case loading
|
||||
|
@ -15,6 +17,9 @@ public struct NotificationsRequestsListView: View {
|
|||
}
|
||||
|
||||
@State private var viewState: ViewState = .loading
|
||||
@State private var isProcessingRequests: Bool = false
|
||||
@State private var isInSelectMode: Bool = false
|
||||
@State private var selectedRequests: [NotificationsRequest] = []
|
||||
|
||||
public init() {}
|
||||
|
||||
|
@ -23,39 +28,27 @@ public struct NotificationsRequestsListView: View {
|
|||
switch viewState {
|
||||
case .loading:
|
||||
ProgressView()
|
||||
#if !os(visionOS)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
#if !os(visionOS)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
.listSectionSeparator(.hidden)
|
||||
case .error:
|
||||
ErrorView(title: "notifications.error.title",
|
||||
message: "notifications.error.message",
|
||||
buttonTitle: "action.retry")
|
||||
{
|
||||
ErrorView(
|
||||
title: "notifications.error.title",
|
||||
message: "notifications.error.message",
|
||||
buttonTitle: "action.retry"
|
||||
) {
|
||||
await fetchRequests(client)
|
||||
}
|
||||
#if !os(visionOS)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
.listSectionSeparator(.hidden)
|
||||
case let .requests(data):
|
||||
ForEach(data) { request in
|
||||
NotificationsRequestsRowView(request: request)
|
||||
.swipeActions {
|
||||
Button {
|
||||
Task { await acceptRequest(client, request) }
|
||||
} label: {
|
||||
Label("account.follow-request.accept", systemImage: "checkmark")
|
||||
}
|
||||
|
||||
Button {
|
||||
Task { await dismissRequest(client, request) }
|
||||
} label: {
|
||||
Label("account.follow-request.reject", systemImage: "xmark")
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
if isInSelectMode {
|
||||
selectSection(data: data)
|
||||
}
|
||||
requestsSection(data: data)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
|
@ -63,14 +56,124 @@ public struct NotificationsRequestsListView: View {
|
|||
.scrollContentBackground(.hidden)
|
||||
.background(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
.navigationTitle("notifications.content-filter.requests.title")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.task {
|
||||
await fetchRequests(client)
|
||||
.navigationTitle("notifications.content-filter.requests.title")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
withAnimation {
|
||||
isInSelectMode.toggle()
|
||||
if !isInSelectMode {
|
||||
selectedRequests.removeAll()
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text(isInSelectMode ? "Cancel" : "Select")
|
||||
}
|
||||
}
|
||||
.refreshable {
|
||||
await fetchRequests(client)
|
||||
}
|
||||
.task {
|
||||
await fetchRequests(client)
|
||||
}
|
||||
.refreshable {
|
||||
await fetchRequests(client)
|
||||
}
|
||||
}
|
||||
|
||||
private func selectSection(data: [NotificationsRequest]) -> some View {
|
||||
Section {
|
||||
HStack(alignment: .center) {
|
||||
Button {
|
||||
withAnimation {
|
||||
selectedRequests = data
|
||||
}
|
||||
} label: {
|
||||
Text("Select all")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
Button {
|
||||
Task { await acceptSelectedRequests(client) }
|
||||
} label: {
|
||||
Text("Accept")
|
||||
}
|
||||
.disabled(isProcessingRequests)
|
||||
.buttonStyle(.borderedProminent)
|
||||
|
||||
Button {
|
||||
Task { await rejectSelectedRequests(client) }
|
||||
} label: {
|
||||
Text("Reject")
|
||||
}
|
||||
.disabled(isProcessingRequests)
|
||||
.buttonStyle(.bordered)
|
||||
.tint(.red)
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
|
||||
private func requestsSection(data: [NotificationsRequest]) -> some View {
|
||||
Section {
|
||||
ForEach(data) { request in
|
||||
let isSelected = selectedRequests.contains(where: { $0.id == request.id })
|
||||
HStack {
|
||||
if isInSelectMode {
|
||||
if isSelected {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.resizable()
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundStyle(theme.tintColor)
|
||||
} else {
|
||||
Circle()
|
||||
.strokeBorder(theme.tintColor, lineWidth: 1)
|
||||
.frame(width: 20, height: 20)
|
||||
}
|
||||
}
|
||||
NotificationsRequestsRowView(request: request)
|
||||
}
|
||||
.onTapGesture {
|
||||
if isInSelectMode {
|
||||
withAnimation {
|
||||
if isSelected {
|
||||
selectedRequests.removeAll { $0.id == request.id }
|
||||
} else {
|
||||
selectedRequests.append(request)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
routerPath.navigate(to: .notificationForAccount(accountId: request.account.id))
|
||||
}
|
||||
}
|
||||
.listRowInsets(
|
||||
.init(
|
||||
top: 12,
|
||||
leading: .layoutPadding,
|
||||
bottom: 12,
|
||||
trailing: .layoutPadding)
|
||||
)
|
||||
#if os(visionOS)
|
||||
.listRowBackground(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.foregroundStyle(.background))
|
||||
#else
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
.swipeActions {
|
||||
Button {
|
||||
Task { await acceptRequest(client, request) }
|
||||
} label: {
|
||||
Label("account.follow-request.accept", systemImage: "checkmark")
|
||||
}
|
||||
|
||||
Button {
|
||||
Task { await dismissRequest(client, request) }
|
||||
} label: {
|
||||
Label("account.follow-request.reject", systemImage: "xmark")
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchRequests(_ client: Client) async {
|
||||
|
@ -82,12 +185,28 @@ public struct NotificationsRequestsListView: View {
|
|||
}
|
||||
|
||||
private func acceptRequest(_ client: Client, _ request: NotificationsRequest) async {
|
||||
_ = try? await client.post(endpoint: Notifications.acceptRequest(id: request.id))
|
||||
_ = try? await client.post(endpoint: Notifications.acceptRequests(ids: [request.id]))
|
||||
await fetchRequests(client)
|
||||
}
|
||||
|
||||
private func dismissRequest(_ client: Client, _ request: NotificationsRequest) async {
|
||||
_ = try? await client.post(endpoint: Notifications.dismissRequest(id: request.id))
|
||||
_ = try? await client.post(endpoint: Notifications.dismissRequests(ids: [request.id]))
|
||||
await fetchRequests(client)
|
||||
}
|
||||
|
||||
private func acceptSelectedRequests(_ client: Client) async {
|
||||
isProcessingRequests = true
|
||||
_ = try? await client.post(
|
||||
endpoint: Notifications.acceptRequests(ids: selectedRequests.map { $0.id }))
|
||||
await fetchRequests(client)
|
||||
isProcessingRequests = false
|
||||
}
|
||||
|
||||
private func rejectSelectedRequests(_ client: Client) async {
|
||||
isProcessingRequests = true
|
||||
_ = try? await client.post(
|
||||
endpoint: Notifications.dismissRequests(ids: selectedRequests.map { $0.id }))
|
||||
await fetchRequests(client)
|
||||
isProcessingRequests = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import SwiftUI
|
|||
|
||||
struct NotificationsRequestsRowView: View {
|
||||
@Environment(Theme.self) private var theme
|
||||
@Environment(RouterPath.self) private var routerPath
|
||||
@Environment(Client.self) private var client
|
||||
|
||||
let request: NotificationsRequest
|
||||
|
@ -39,18 +38,5 @@ struct NotificationsRequestsRowView: View {
|
|||
Image(systemName: "chevron.right")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.onTapGesture {
|
||||
routerPath.navigate(to: .notificationForAccount(accountId: request.account.id))
|
||||
}
|
||||
.listRowInsets(.init(top: 12,
|
||||
leading: .layoutPadding,
|
||||
bottom: 12,
|
||||
trailing: .layoutPadding))
|
||||
#if os(visionOS)
|
||||
.listRowBackground(RoundedRectangle(cornerRadius: 8)
|
||||
.foregroundStyle(.background))
|
||||
#else
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue