mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-25 09:41:02 +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 policy
|
||||||
case putPolicy(policy: Models.NotificationsPolicy)
|
case putPolicy(policy: Models.NotificationsPolicy)
|
||||||
case requests
|
case requests
|
||||||
case acceptRequest(id: String)
|
case acceptRequests(ids: [String])
|
||||||
case dismissRequest(id: String)
|
case dismissRequests(ids: [String])
|
||||||
case clear
|
case clear
|
||||||
|
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
|
@ -25,10 +25,10 @@ public enum Notifications: Endpoint {
|
||||||
"notifications/policy"
|
"notifications/policy"
|
||||||
case .requests:
|
case .requests:
|
||||||
"notifications/requests"
|
"notifications/requests"
|
||||||
case let .acceptRequest(id):
|
case .acceptRequests:
|
||||||
"notifications/requests/\(id)/accept"
|
"notifications/requests/accept"
|
||||||
case let .dismissRequest(id):
|
case .dismissRequests:
|
||||||
"notifications/requests/\(id)/dismiss"
|
"notifications/requests/dismiss"
|
||||||
case .clear:
|
case .clear:
|
||||||
"notifications/clear"
|
"notifications/clear"
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,8 @@ public enum Notifications: Endpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
|
case let .acceptRequests(ids), let .dismissRequests(ids):
|
||||||
|
return ids.map { URLQueryItem(name: "id[]", value: $0) }
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
|
import Env
|
||||||
import Models
|
import Models
|
||||||
import Network
|
import Network
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
@ -7,6 +8,7 @@ import SwiftUI
|
||||||
public struct NotificationsRequestsListView: View {
|
public struct NotificationsRequestsListView: View {
|
||||||
@Environment(Client.self) private var client
|
@Environment(Client.self) private var client
|
||||||
@Environment(Theme.self) private var theme
|
@Environment(Theme.self) private var theme
|
||||||
|
@Environment(RouterPath.self) private var routerPath
|
||||||
|
|
||||||
enum ViewState {
|
enum ViewState {
|
||||||
case loading
|
case loading
|
||||||
|
@ -15,6 +17,9 @@ public struct NotificationsRequestsListView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
@State private var viewState: ViewState = .loading
|
@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() {}
|
public init() {}
|
||||||
|
|
||||||
|
@ -28,10 +33,11 @@ public struct NotificationsRequestsListView: View {
|
||||||
#endif
|
#endif
|
||||||
.listSectionSeparator(.hidden)
|
.listSectionSeparator(.hidden)
|
||||||
case .error:
|
case .error:
|
||||||
ErrorView(title: "notifications.error.title",
|
ErrorView(
|
||||||
|
title: "notifications.error.title",
|
||||||
message: "notifications.error.message",
|
message: "notifications.error.message",
|
||||||
buttonTitle: "action.retry")
|
buttonTitle: "action.retry"
|
||||||
{
|
) {
|
||||||
await fetchRequests(client)
|
await fetchRequests(client)
|
||||||
}
|
}
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
|
@ -39,8 +45,119 @@ public struct NotificationsRequestsListView: View {
|
||||||
#endif
|
#endif
|
||||||
.listSectionSeparator(.hidden)
|
.listSectionSeparator(.hidden)
|
||||||
case let .requests(data):
|
case let .requests(data):
|
||||||
|
if isInSelectMode {
|
||||||
|
selectSection(data: data)
|
||||||
|
}
|
||||||
|
requestsSection(data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.listStyle(.plain)
|
||||||
|
#if !os(visionOS)
|
||||||
|
.scrollContentBackground(.hidden)
|
||||||
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
.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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.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
|
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)
|
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 {
|
.swipeActions {
|
||||||
Button {
|
Button {
|
||||||
Task { await acceptRequest(client, request) }
|
Task { await acceptRequest(client, request) }
|
||||||
|
@ -58,20 +175,6 @@ public struct NotificationsRequestsListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listStyle(.plain)
|
|
||||||
#if !os(visionOS)
|
|
||||||
.scrollContentBackground(.hidden)
|
|
||||||
.background(theme.primaryBackgroundColor)
|
|
||||||
#endif
|
|
||||||
.navigationTitle("notifications.content-filter.requests.title")
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
.task {
|
|
||||||
await fetchRequests(client)
|
|
||||||
}
|
|
||||||
.refreshable {
|
|
||||||
await fetchRequests(client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func fetchRequests(_ client: Client) async {
|
private func fetchRequests(_ client: Client) async {
|
||||||
do {
|
do {
|
||||||
|
@ -82,12 +185,28 @@ public struct NotificationsRequestsListView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func acceptRequest(_ client: Client, _ request: NotificationsRequest) async {
|
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)
|
await fetchRequests(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dismissRequest(_ client: Client, _ request: NotificationsRequest) async {
|
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)
|
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 {
|
struct NotificationsRequestsRowView: View {
|
||||||
@Environment(Theme.self) private var theme
|
@Environment(Theme.self) private var theme
|
||||||
@Environment(RouterPath.self) private var routerPath
|
|
||||||
@Environment(Client.self) private var client
|
@Environment(Client.self) private var client
|
||||||
|
|
||||||
let request: NotificationsRequest
|
let request: NotificationsRequest
|
||||||
|
@ -39,18 +38,5 @@ struct NotificationsRequestsRowView: View {
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
.foregroundStyle(.secondary)
|
.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