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.
 
 
PadelClub/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift

333 lines
11 KiB

//
// ToolboxView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 29/02/2024.
//
import SwiftUI
import LeStorage
import Zip
struct ToolboxView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(NavigationViewModel.self) private var navigation: NavigationViewModel
@State private var didResetApiCalls: Bool = false
@State var showDebugViews: Bool = false
@State private var tapCount = 0
@State private var lastTapTime: Date? = nil
private let tapTimeThreshold: TimeInterval = 1.0
var lastDataSource: String? {
dataStore.appSettings.lastDataSource
}
var _mostRecentDateAvailable: Date? {
SourceFileManager.shared.mostRecentDateAvailable
}
var _lastDataSourceDate: Date? {
guard let lastDataSource else { return nil }
return URL.importDateFormatter.date(from: lastDataSource)
}
var body: some View {
@Bindable var navigation = navigation
NavigationStack(path: $navigation.toolboxPath) {
List {
Section {
Link(destination: URLs.main.url) {
Text("Accéder à padelclub.app")
}
.contextMenu {
ShareLink(item: URLs.main.url)
}
SupportButtonView(contentIsUnavailable: false)
Link(destination: URLs.appReview.url) {
Text("Partagez vos impressions !")
}
Link(destination: URLs.instagram.url) {
Text("Compte Instagram PadelClub.app")
}
}
if showDebugViews {
DebugView()
}
Section {
NavigationLink {
SelectablePlayerListView(isPresented: false, lastDataSource: true)
} label: {
Label("Rechercher un joueur", systemImage: "person.fill.viewfinder")
}
NavigationLink {
RankCalculatorView()
} label: {
Label("Calculateur de points", systemImage: "scalemass")
}
}
Section {
NavigationLink {
PadelClubView()
} label: {
if let _lastDataSourceDate {
LabeledContent {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green)
} label: {
Text(_lastDataSourceDate.monthYearFormatted)
Text("Classement mensuel utilisé")
}
} else {
LabeledContent {
Image(systemName: "xmark.circle.fill")
.tint(.logoRed)
} label: {
if let _mostRecentDateAvailable {
Text(_mostRecentDateAvailable.monthYearFormatted)
} else {
Text("Aucun")
}
Text("Classement mensuel disponible")
}
}
}
}
Section {
Link("Accéder au guide de la compétition", destination: URLs.padelCompetitionGeneralGuide.url)
Link("Accéder aux CDC des tournois", destination: URLs.padelCompetitionSpecificGuide.url)
Link("Accéder aux règles du jeu", destination: URLs.padelRules.url)
Link("Décharge des temps de repos", destination: URLs.restingDischarge.url)
} header: {
Text("Documents fédéraux")
.onTapGesture(count: 5) {
StoreCenter.main.resetApiCalls()
didResetApiCalls = true
}
}
Section {
Link(destination: URLs.appDescription.url) {
Text("Page de présentation de Padel Club")
}
}
Section {
Link(destination: URLs.privacy.url) {
Text("Politique de confidentialité")
}
Link(destination: URLs.eula.url) {
Text("Contrat d'utilisation")
}
}
Section {
RowButtonView("Effacer les logs", role: .destructive) {
StoreCenter.main.resetLoggingCollections()
didResetApiCalls = true
}
}
}
.onAppear {
//#if DEBUG
// self.showDebugViews = true
//#endif
//
//#if TESTFLIGHT
// self.showDebugViews = true
//#endif
}
.overlay(alignment: .bottom) {
if didResetApiCalls {
Label("logs effacés", systemImage: "checkmark")
.toastFormatted()
.deferredRendering(for: .seconds(3))
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
didResetApiCalls = false
}
}
}
}
.navigationBarTitleDisplayMode(.large)
// .navigationTitle(TabDestination.toolbox.title)
.toolbar {
ToolbarItem(placement: .principal) {
Text(TabDestination.toolbox.title)
.font(.headline)
.onTapGesture {
_handleTitleTap()
}
}
ToolbarItem(placement: .topBarLeading) {
Link(destination: URLs.appStore.url) {
Text("v\(PadelClubApp.appVersion)")
}
}
ToolbarItem(placement: .topBarTrailing) {
Menu {
ShareLink(item: URLs.appStore.url) {
Label("Lien AppStore", systemImage: "link")
}
ShareLink(item: ZipLog(), preview: .init("Mon archive")) {
Label("Mes données", systemImage: "server.rack")
}
} label: {
Label("Partagez", systemImage: "square.and.arrow.up").labelStyle(.iconOnly)
}
}
}
}
}
private func _handleTitleTap() {
// Reset counter if too much time elapsed since last tap
if let lastTime = lastTapTime, Date().timeIntervalSince(lastTime) > tapTimeThreshold {
tapCount = 0
}
// Update tap count and time
tapCount += 1
lastTapTime = Date()
print("Tap count: \(tapCount)")
// Check if we've reached 4 taps
if tapCount == 4 {
_secretFeatureActivated()
tapCount = 0
}
}
private func _secretFeatureActivated() {
self.showDebugViews = true
}
}
//#Preview {
// ToolboxView()
//}
struct DebugView: View {
@EnvironmentObject var dataStore: DataStore
var body: some View {
Section {
NavigationLink("Settings") {
DebugSettingsView()
}
NavigationLink("API calls") {
APICallsListView()
}
}
Section {
RowButtonView("Reset ALL API Calls") {
StoreCenter.main.resetApiCalls()
Logger.log("Api calls reset")
}
}
Section {
RowButtonView("Fix Names") {
for tournament in dataStore.tournaments {
if let store = tournament.tournamentStore {
let playerRegistrations = store.playerRegistrations
playerRegistrations.forEach { player in
player.firstName = player.firstName.trimmed.capitalized
player.lastName = player.lastName.trimmed.uppercased()
}
do {
try store.playerRegistrations.addOrUpdate(contentOfs: playerRegistrations)
} catch {
Logger.error(error)
}
}
}
}
}
Section {
RowButtonView("Delete teams") {
for tournament in DataStore.shared.tournaments {
if let store: TournamentStore = tournament.tournamentStore {
let teamRegistrations = store.teamRegistrations.filter({ $0.tournamentObject() == nil })
do {
try store.teamRegistrations.delete(contentOfs: teamRegistrations)
} catch {
Logger.error(error)
}
}
}
}
}
Section {
// TODO
RowButtonView("Delete players") {
for tournament in DataStore.shared.tournaments {
if let store: TournamentStore = tournament.tournamentStore {
let playersRegistrations = store.playerRegistrations.filter({ $0.team() == nil })
do {
try store.playerRegistrations.delete(contentOfs: playersRegistrations)
} catch {
Logger.error(error)
}
}
}
}
}
}
}
struct ZipLog: Transferable {
private func _getZip() -> URL? {
do {
let filePath = try Club.storageDirectoryPath()
return try Zip.quickZipFiles([filePath], fileName: "backup") // Zip
} catch {
Logger.error(error)
return nil
}
}
func shareFile() -> URL? {
print("Generating URL...")
return _getZip()
}
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(exportedContentType: .zip) { transferable in
return SentTransferredFile(transferable.shareFile()!)
}.exportingCondition { $0.shareFile() != nil }
ProxyRepresentation { transferable in
return transferable.shareFile()!
}.exportingCondition { $0.shareFile() != nil }
}
}