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.
333 lines
11 KiB
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 }
|
|
}
|
|
}
|
|
|