You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
7.5 KiB
244 lines
7.5 KiB
//
|
|
// APICallsView.swift
|
|
// PadelClub
|
|
//
|
|
// Created by Laurent Morvillier on 27/05/2024.
|
|
//
|
|
|
|
import SwiftUI
|
|
import LeStorage
|
|
import PadelClubData
|
|
|
|
struct APICallsListView: View {
|
|
|
|
@State var descriptors: [CollectionDescriptor] = []
|
|
@State var total = 0
|
|
|
|
var body: some View {
|
|
|
|
List {
|
|
Section {
|
|
LabeledContent("Total count", value: "\(total)")
|
|
}
|
|
|
|
Section {
|
|
ForEach(self.descriptors) { descriptor in
|
|
|
|
NavigationLink {
|
|
if let syncedType = descriptor.type as? any SyncedStorable.Type {
|
|
APICallsView(name: descriptor.name, type: syncedType)
|
|
}
|
|
} label: {
|
|
LabeledContent(descriptor.name, value: descriptor.count.string)
|
|
}
|
|
|
|
}
|
|
}
|
|
}.onAppear {
|
|
self.load()
|
|
}
|
|
}
|
|
|
|
func load() {
|
|
Task {
|
|
self.descriptors = await StoreCenter.main.apiCollectionDescriptors().map {
|
|
CollectionDescriptor(name: $0.0, type: $0.1)
|
|
}.sorted(by: \.name, order: .ascending)
|
|
|
|
for descriptor in self.descriptors {
|
|
if let type = descriptor.type as? any SyncedStorable.Type {
|
|
await self.loadCount(type, descriptor)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func loadCount<T: SyncedStorable>(_ type: T.Type, _ descriptor: CollectionDescriptor) async {
|
|
let calls = await StoreCenter.main.apiCalls(type: type)
|
|
descriptor.count = calls.count
|
|
|
|
self.total = total + descriptor.count
|
|
// Logger.log("\(descriptor.name), count = \(calls.count)")
|
|
}
|
|
|
|
}
|
|
|
|
@Observable
|
|
class CollectionDescriptor: Identifiable, Hashable {
|
|
|
|
var id: UUID = UUID()
|
|
var name: String
|
|
var type: any Storable.Type
|
|
var count: Int = 0
|
|
|
|
init(name: String, type: any Storable.Type) {
|
|
self.name = name
|
|
self.type = type
|
|
}
|
|
|
|
static func == (lhs: CollectionDescriptor, rhs: CollectionDescriptor) -> Bool {
|
|
return lhs.id == rhs.id
|
|
}
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(self.id)
|
|
}
|
|
|
|
}
|
|
|
|
struct APICallsView: View {
|
|
|
|
var name: String
|
|
var type: any SyncedStorable.Type
|
|
|
|
// @State var text: String = ""
|
|
@State var apiCalls: [any SomeCall] = []
|
|
|
|
var body: some View {
|
|
List {
|
|
Section(header:
|
|
Text("\(apiCalls.count) API calls")
|
|
.font(.headline)
|
|
.foregroundColor(.primary)
|
|
) {
|
|
ForEach(self.apiCalls, id: \.id) { apiCall in
|
|
NavigationLink {
|
|
APICallView(apiCall: apiCall, type: self.type)
|
|
} label: {
|
|
LabeledContent(apiCall.method.rawValue, value: apiCall.attemptsCount.string)
|
|
}
|
|
}
|
|
}
|
|
}.navigationTitle(self.name)
|
|
.onAppear {
|
|
self._load()
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
fileprivate func _load() {
|
|
Task {
|
|
await self.load(self.type)
|
|
// self.text = await StoreCenter.main.apiCallsFileContent(resourceName: self.name)
|
|
}
|
|
}
|
|
|
|
func load<T: SyncedStorable>(_ type: T.Type) async {
|
|
let calls: [ApiCall<T>] = await StoreCenter.main.apiCalls(type: type)
|
|
self.apiCalls = calls
|
|
}
|
|
|
|
}
|
|
|
|
struct APICallView: View {
|
|
|
|
var apiCall: any SomeCall
|
|
var type: any SyncedStorable.Type
|
|
@State private var errorMessage: String? = nil
|
|
@State private var isLoading: Bool = false
|
|
@State private var showSuccessPopover: Bool = false
|
|
|
|
var body: some View {
|
|
Form {
|
|
Section {
|
|
LabeledContent("Method", value: apiCall.method.rawValue)
|
|
LabeledContent("Data id", value: apiCall.stringId)
|
|
LabeledContent("Attempts", value: apiCall.attemptsCount.string)
|
|
}
|
|
|
|
Section {
|
|
if isLoading {
|
|
ProgressView()
|
|
.padding()
|
|
} else {
|
|
|
|
Button("Execute") {
|
|
self.execute()
|
|
// Clear previous error when attempting again
|
|
errorMessage = nil
|
|
}
|
|
.disabled(isLoading)
|
|
}
|
|
if let errorMessage = errorMessage {
|
|
LabeledContent("Error", value: errorMessage)
|
|
// VStack(alignment: .leading) {
|
|
// Text("Error:")
|
|
// .fontWeight(.bold)
|
|
// .foregroundColor(.red)
|
|
// Text(errorMessage)
|
|
// .foregroundColor(.red)
|
|
// }
|
|
}
|
|
if showSuccessPopover {
|
|
VStack {
|
|
Image(systemName: "checkmark.circle.fill")
|
|
.foregroundColor(.green)
|
|
.font(.title)
|
|
Text("Success")
|
|
.foregroundColor(.green)
|
|
}
|
|
.padding()
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 8)
|
|
.fill(Color.white)
|
|
.shadow(radius: 2)
|
|
)
|
|
.transition(.scale.combined(with: .opacity))
|
|
}
|
|
|
|
}
|
|
Section {
|
|
Text(apiCall.dataContent ?? "none").monospaced().font(.caption)
|
|
}
|
|
|
|
}
|
|
.animation(.easeInOut, value: showSuccessPopover)
|
|
.navigationTitle(String(describing: self.type))
|
|
}
|
|
|
|
func execute() {
|
|
self.execute(type: self.type)
|
|
}
|
|
|
|
func execute<T: SyncedStorable>(type: T.Type) {
|
|
if let call = self.apiCall as? ApiCall<T> {
|
|
Task {
|
|
await MainActor.run {
|
|
isLoading = true
|
|
}
|
|
|
|
do {
|
|
let results = try await StoreCenter.main.execute(apiCalls: [call])
|
|
|
|
await MainActor.run {
|
|
isLoading = false
|
|
if let result = results.first {
|
|
errorMessage = result.message
|
|
showSuccessPopover = (200..<300).contains(result.status)
|
|
} else {
|
|
errorMessage = "no results"
|
|
}
|
|
}
|
|
|
|
// Update UI on the main thread
|
|
// await MainActor.run {
|
|
// errorMessage = nil
|
|
// isLoading = false
|
|
//
|
|
// // Show success popover
|
|
// showSuccessPopover = true
|
|
// }
|
|
} catch {
|
|
// Update UI on the main thread
|
|
await MainActor.run {
|
|
errorMessage = error.localizedDescription
|
|
isLoading = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//#Preview {
|
|
// APICallsListView()
|
|
//}
|
|
|