fix micro storage
multistore
Razmig Sarkissian 2 years ago
parent f13a71675d
commit b9f2048546
  1. 28
      PadelClub.xcodeproj/project.pbxproj
  2. 36
      PadelClub/Data/AppSettings.swift
  3. 9
      PadelClub/Data/Club.swift
  4. 41
      PadelClub/Data/DataStore.swift
  5. 1
      PadelClub/Data/Event.swift
  6. 1
      PadelClub/Data/Federal/FederalTournament.swift
  7. 11
      PadelClub/Data/GroupStage.swift
  8. 8
      PadelClub/Data/Match.swift
  9. 9
      PadelClub/Data/MockData.swift
  10. 53
      PadelClub/Data/MonthData.swift
  11. 9
      PadelClub/Data/Round.swift
  12. 42
      PadelClub/Data/Tournament.swift
  13. 11
      PadelClub/Data/User.swift
  14. 14
      PadelClub/Manager/ContactManager.swift
  15. 14
      PadelClub/Manager/PadelRule.swift
  16. 2
      PadelClub/Manager/SourceFileManager.swift
  17. 13
      PadelClub/ViewModel/AppScreen.swift
  18. 8
      PadelClub/ViewModel/MatchScheduler.swift
  19. 1
      PadelClub/ViewModel/NavigationViewModel.swift
  20. 38
      PadelClub/Views/Calling/CallMessageCustomizationView.swift
  21. 4
      PadelClub/Views/Calling/CallSettingsView.swift
  22. 1
      PadelClub/Views/Club/ClubSearchView.swift
  23. 23
      PadelClub/Views/ClubView.swift
  24. 4
      PadelClub/Views/Components/Labels.swift
  25. 8
      PadelClub/Views/Components/StepperView.swift
  26. 82
      PadelClub/Views/ContentView.swift
  27. 6
      PadelClub/Views/Match/Components/MatchDateView.swift
  28. 6
      PadelClub/Views/Match/MatchDetailView.swift
  29. 6
      PadelClub/Views/Navigation/Agenda/ActivityView.swift
  30. 27
      PadelClub/Views/Navigation/MainView.swift
  31. 1
      PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift
  32. 48
      PadelClub/Views/Navigation/PadelClubView.swift
  33. 25
      PadelClub/Views/Navigation/Toolbox/DurationSettingsView.swift
  34. 67
      PadelClub/Views/Navigation/Toolbox/GlobalSettingsView.swift
  35. 50
      PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift
  36. 17
      PadelClub/Views/Navigation/Toolbox/ToolboxView.swift
  37. 2
      PadelClub/Views/Navigation/Umpire/UmpireView.swift
  38. 5
      PadelClub/Views/Planning/PlanningSettingsView.swift
  39. 3
      PadelClub/Views/Shared/MatchFormatPickerView.swift
  40. 7
      PadelClub/Views/Shared/SelectablePlayerListView.swift
  41. 2
      PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift
  42. 24
      PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift
  43. 86
      PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift
  44. 2
      PadelClub/Views/Tournament/TournamentInitView.swift
  45. 6
      PadelClub/Views/Tournament/TournamentView.swift

@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
C425D4012B6D249D002A7B48 /* PadelClubApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4002B6D249D002A7B48 /* PadelClubApp.swift */; }; C425D4012B6D249D002A7B48 /* PadelClubApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4002B6D249D002A7B48 /* PadelClubApp.swift */; };
C425D4032B6D249D002A7B48 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4022B6D249D002A7B48 /* ContentView.swift */; };
C425D4052B6D249E002A7B48 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4042B6D249E002A7B48 /* Assets.xcassets */; }; C425D4052B6D249E002A7B48 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4042B6D249E002A7B48 /* Assets.xcassets */; };
C425D4082B6D249E002A7B48 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4072B6D249E002A7B48 /* Preview Assets.xcassets */; }; C425D4082B6D249E002A7B48 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4072B6D249E002A7B48 /* Preview Assets.xcassets */; };
C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4112B6D249E002A7B48 /* PadelClubTests.swift */; }; C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4112B6D249E002A7B48 /* PadelClubTests.swift */; };
@ -20,7 +19,6 @@
C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D592B6D383C00ADC637 /* Tournament.swift */; }; C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D592B6D383C00ADC637 /* Tournament.swift */; };
C4A47D5E2B6D38EC00ADC637 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */; }; C4A47D5E2B6D38EC00ADC637 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */; };
C4A47D632B6D3D6500ADC637 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D622B6D3D6500ADC637 /* Club.swift */; }; C4A47D632B6D3D6500ADC637 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D622B6D3D6500ADC637 /* Club.swift */; };
C4A47D742B72881F00ADC637 /* ClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D732B72881F00ADC637 /* ClubView.swift */; };
C4A47D772B73789100ADC637 /* TournamentV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D762B73789100ADC637 /* TournamentV1.swift */; }; C4A47D772B73789100ADC637 /* TournamentV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D762B73789100ADC637 /* TournamentV1.swift */; };
C4A47D7B2B73C0F900ADC637 /* TournamentV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D7A2B73C0F900ADC637 /* TournamentV2.swift */; }; C4A47D7B2B73C0F900ADC637 /* TournamentV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D7A2B73C0F900ADC637 /* TournamentV2.swift */; };
C4A47D7D2B73CDC300ADC637 /* ClubV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D7C2B73CDC300ADC637 /* ClubV1.swift */; }; C4A47D7D2B73CDC300ADC637 /* ClubV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D7C2B73CDC300ADC637 /* ClubV1.swift */; };
@ -42,6 +40,11 @@
FF025AE12BD0EB9000A86CF8 /* TournamentClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */; }; FF025AE12BD0EB9000A86CF8 /* TournamentClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */; };
FF025AE32BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */; }; FF025AE32BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */; };
FF025AE52BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */; }; FF025AE52BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */; };
FF025AE72BD1111000A86CF8 /* GlobalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */; };
FF025AE92BD1307F00A86CF8 /* MonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE82BD1307E00A86CF8 /* MonthData.swift */; };
FF025AED2BD1513700A86CF8 /* AppScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEC2BD1513700A86CF8 /* AppScreen.swift */; };
FF025AEF2BD1AE9400A86CF8 /* DurationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */; };
FF025AF12BD1AEBD00A86CF8 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; };
FF089EB42BB0020000F0AEC7 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; }; FF089EB42BB0020000F0AEC7 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; };
FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; }; FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; };
FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; }; FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; };
@ -290,7 +293,6 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
C425D3FD2B6D249D002A7B48 /* PadelClub.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PadelClub.app; sourceTree = BUILT_PRODUCTS_DIR; }; C425D3FD2B6D249D002A7B48 /* PadelClub.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PadelClub.app; sourceTree = BUILT_PRODUCTS_DIR; };
C425D4002B6D249D002A7B48 /* PadelClubApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubApp.swift; sourceTree = "<group>"; }; C425D4002B6D249D002A7B48 /* PadelClubApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubApp.swift; sourceTree = "<group>"; };
C425D4022B6D249D002A7B48 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
C425D4042B6D249E002A7B48 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; C425D4042B6D249E002A7B48 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C425D4072B6D249E002A7B48 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; }; C425D4072B6D249E002A7B48 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
C425D40D2B6D249E002A7B48 /* PadelClubTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PadelClubTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C425D40D2B6D249E002A7B48 /* PadelClubTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PadelClubTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@ -305,7 +307,6 @@
C4A47D592B6D383C00ADC637 /* Tournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tournament.swift; sourceTree = "<group>"; }; C4A47D592B6D383C00ADC637 /* Tournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tournament.swift; sourceTree = "<group>"; };
C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStore.swift; sourceTree = "<group>"; }; C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStore.swift; sourceTree = "<group>"; };
C4A47D622B6D3D6500ADC637 /* Club.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Club.swift; sourceTree = "<group>"; }; C4A47D622B6D3D6500ADC637 /* Club.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Club.swift; sourceTree = "<group>"; };
C4A47D732B72881F00ADC637 /* ClubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubView.swift; sourceTree = "<group>"; };
C4A47D762B73789100ADC637 /* TournamentV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentV1.swift; sourceTree = "<group>"; }; C4A47D762B73789100ADC637 /* TournamentV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentV1.swift; sourceTree = "<group>"; };
C4A47D7A2B73C0F900ADC637 /* TournamentV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentV2.swift; sourceTree = "<group>"; }; C4A47D7A2B73C0F900ADC637 /* TournamentV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentV2.swift; sourceTree = "<group>"; };
C4A47D7C2B73CDC300ADC637 /* ClubV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubV1.swift; sourceTree = "<group>"; }; C4A47D7C2B73CDC300ADC637 /* ClubV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubV1.swift; sourceTree = "<group>"; };
@ -327,6 +328,11 @@
FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentClubSettingsView.swift; sourceTree = "<group>"; }; FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentClubSettingsView.swift; sourceTree = "<group>"; };
FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentMatchFormatsSettingsView.swift; sourceTree = "<group>"; }; FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentMatchFormatsSettingsView.swift; sourceTree = "<group>"; };
FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentGeneralSettingsView.swift; sourceTree = "<group>"; }; FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentGeneralSettingsView.swift; sourceTree = "<group>"; };
FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSettingsView.swift; sourceTree = "<group>"; };
FF025AE82BD1307E00A86CF8 /* MonthData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthData.swift; sourceTree = "<group>"; };
FF025AEC2BD1513700A86CF8 /* AppScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppScreen.swift; sourceTree = "<group>"; };
FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DurationSettingsView.swift; sourceTree = "<group>"; };
FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatStorageView.swift; sourceTree = "<group>"; };
FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSexPickerView.swift; sourceTree = "<group>"; }; FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSexPickerView.swift; sourceTree = "<group>"; };
FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamRowView.swift; sourceTree = "<group>"; }; FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamRowView.swift; sourceTree = "<group>"; };
FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerPopoverView.swift; sourceTree = "<group>"; }; FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerPopoverView.swift; sourceTree = "<group>"; };
@ -650,6 +656,7 @@
FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */, FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */,
C4A47D622B6D3D6500ADC637 /* Club.swift */, C4A47D622B6D3D6500ADC637 /* Club.swift */,
FF8F263E2BAD7D5C00650388 /* Event.swift */, FF8F263E2BAD7D5C00650388 /* Event.swift */,
FF025AE82BD1307E00A86CF8 /* MonthData.swift */,
FF1DC5522BAB354A00FD8220 /* MockData.swift */, FF1DC5522BAB354A00FD8220 /* MockData.swift */,
FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */, FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */,
FF6EC9012B94799200EA7F5A /* Coredata */, FF6EC9012B94799200EA7F5A /* Coredata */,
@ -661,8 +668,6 @@
C4A47D722B72881500ADC637 /* Views */ = { C4A47D722B72881500ADC637 /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C425D4022B6D249D002A7B48 /* ContentView.swift */,
C4A47D732B72881F00ADC637 /* ClubView.swift */,
FF39719B2B8DE04B004C4E75 /* Navigation */, FF39719B2B8DE04B004C4E75 /* Navigation */,
FF8F26392BAD526A00650388 /* Event */, FF8F26392BAD526A00650388 /* Event */,
FF1DC54D2BAB34FA00FD8220 /* Club */, FF1DC54D2BAB34FA00FD8220 /* Club */,
@ -920,6 +925,9 @@
children = ( children = (
FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */, FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */,
FF5D0D822BB48997005CB568 /* RankCalculatorView.swift */, FF5D0D822BB48997005CB568 /* RankCalculatorView.swift */,
FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */,
FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */,
FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */,
); );
path = Toolbox; path = Toolbox;
sourceTree = "<group>"; sourceTree = "<group>";
@ -936,6 +944,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FF7091652B90F0B000AB08DA /* TabDestination.swift */, FF7091652B90F0B000AB08DA /* TabDestination.swift */,
FF025AEC2BD1513700A86CF8 /* AppScreen.swift */,
FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */, FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */,
FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */, FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */,
FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */, FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */,
@ -1414,6 +1423,8 @@
FF70916C2B91005400AB08DA /* TournamentView.swift in Sources */, FF70916C2B91005400AB08DA /* TournamentView.swift in Sources */,
FF1DC5552BAB36DD00FD8220 /* CreateClubView.swift in Sources */, FF1DC5552BAB36DD00FD8220 /* CreateClubView.swift in Sources */,
FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */, FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */,
FF025AEF2BD1AE9400A86CF8 /* DurationSettingsView.swift in Sources */,
FF025AED2BD1513700A86CF8 /* AppScreen.swift in Sources */,
FFCFC00E2BBC3D4600B82851 /* PointSelectionView.swift in Sources */, FFCFC00E2BBC3D4600B82851 /* PointSelectionView.swift in Sources */,
FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */, FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */,
FF92680B2BCEE3E10080F940 /* ContactManager.swift in Sources */, FF92680B2BCEE3E10080F940 /* ContactManager.swift in Sources */,
@ -1426,7 +1437,6 @@
C44B79112BBDA63A00906534 /* Locale+Extensions.swift in Sources */, C44B79112BBDA63A00906534 /* Locale+Extensions.swift in Sources */,
FF967CEA2BAEC70100A9A3BD /* GroupStage.swift in Sources */, FF967CEA2BAEC70100A9A3BD /* GroupStage.swift in Sources */,
FF1162812BCF945C000C4809 /* TournamentCashierView.swift in Sources */, FF1162812BCF945C000C4809 /* TournamentCashierView.swift in Sources */,
C4A47D742B72881F00ADC637 /* ClubView.swift in Sources */,
C4A47D902B7BBBEC00ADC637 /* StoreManager.swift in Sources */, C4A47D902B7BBBEC00ADC637 /* StoreManager.swift in Sources */,
FF11627F2BCF9432000C4809 /* PlayerListView.swift in Sources */, FF11627F2BCF9432000C4809 /* PlayerListView.swift in Sources */,
FF4AB6BB2B9256D50002987F /* SearchViewModel.swift in Sources */, FF4AB6BB2B9256D50002987F /* SearchViewModel.swift in Sources */,
@ -1489,11 +1499,11 @@
FF025AE32BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift in Sources */, FF025AE32BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift in Sources */,
FF11628A2BD05247000C4809 /* DateUpdateManagerView.swift in Sources */, FF11628A2BD05247000C4809 /* DateUpdateManagerView.swift in Sources */,
FFCFC01A2BBC5A8500B82851 /* MatchTypeSmallSelectionView.swift in Sources */, FFCFC01A2BBC5A8500B82851 /* MatchTypeSmallSelectionView.swift in Sources */,
FF025AE92BD1307F00A86CF8 /* MonthData.swift in Sources */,
FF967D0B2BAF3D4C00A9A3BD /* TeamPickerView.swift in Sources */, FF967D0B2BAF3D4C00A9A3BD /* TeamPickerView.swift in Sources */,
FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */, FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */,
FF1DC55B2BAB80C400FD8220 /* DisplayContext.swift in Sources */, FF1DC55B2BAB80C400FD8220 /* DisplayContext.swift in Sources */,
FF9268072BCE94D90080F940 /* TournamentCallView.swift in Sources */, FF9268072BCE94D90080F940 /* TournamentCallView.swift in Sources */,
C425D4032B6D249D002A7B48 /* ContentView.swift in Sources */,
FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */, FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */,
FF967CFC2BAEE52E00A9A3BD /* GroupStagesView.swift in Sources */, FF967CFC2BAEE52E00A9A3BD /* GroupStagesView.swift in Sources */,
FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */, FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */,
@ -1506,6 +1516,7 @@
FFDDD40C2B93B2BB00C91A49 /* DeferredViewModifier.swift in Sources */, FFDDD40C2B93B2BB00C91A49 /* DeferredViewModifier.swift in Sources */,
FFD784042B91C280000F62A6 /* EmptyActivityView.swift in Sources */, FFD784042B91C280000F62A6 /* EmptyActivityView.swift in Sources */,
FF0E0B6D2BC254C6005F00A9 /* TournamentScheduleView.swift in Sources */, FF0E0B6D2BC254C6005F00A9 /* TournamentScheduleView.swift in Sources */,
FF025AF12BD1AEBD00A86CF8 /* MatchFormatStorageView.swift in Sources */,
FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */, FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */,
C4A47D772B73789100ADC637 /* TournamentV1.swift in Sources */, C4A47D772B73789100ADC637 /* TournamentV1.swift in Sources */,
C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */, C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */,
@ -1548,6 +1559,7 @@
FF5D0D722BB3EFA5005CB568 /* LearnMoreSheetView.swift in Sources */, FF5D0D722BB3EFA5005CB568 /* LearnMoreSheetView.swift in Sources */,
FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */, FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */,
FF0EC5222BB173E70056B6D1 /* UpdateSourceRankDateView.swift in Sources */, FF0EC5222BB173E70056B6D1 /* UpdateSourceRankDateView.swift in Sources */,
FF025AE72BD1111000A86CF8 /* GlobalSettingsView.swift in Sources */,
C4A47D912B7BBBEC00ADC637 /* Guard.swift in Sources */, C4A47D912B7BBBEC00ADC637 /* Guard.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

@ -7,14 +7,48 @@
import Foundation import Foundation
import LeStorage import LeStorage
import SwiftUI
@Observable
class AppSettings: MicroStorable { class AppSettings: MicroStorable {
static var fileName: String { "appsettings.json" } static var fileName: String { "appsettings.json" }
var lastDataSource: String? = nil
var callMessageBody : String? = nil
var callMessageSignature: String? = nil
var callDisplayFormat: Bool = false
var callDisplayEntryFee: Bool = false
var callUseFullCustomMessage: Bool = false
var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil
var bracketMatchFormatPreference: Int?
var groupStageMatchFormatPreference: Int?
var loserBracketMatchFormatPreference: Int?
required init() { required init() {
} }
// var id: String = Store.randomId() func saveMatchFormatsDefaultDuration(_ matchFormat: MatchFormat, estimatedDuration: Int) {
if estimatedDuration == matchFormat.defaultEstimatedDuration {
matchFormatsDefaultDuration?.removeValue(forKey: matchFormat)
} else {
matchFormatsDefaultDuration = matchFormatsDefaultDuration ?? [MatchFormat: Int]()
matchFormatsDefaultDuration?[matchFormat] = estimatedDuration
}
}
enum CodingKeys: String, CodingKey {
case _lastDataSource = "lastDataSource"
case _callMessageBody = "callMessageBody"
case _callMessageSignature = "callMessageSignature"
case _callDisplayFormat = "callDisplayFormat"
case _callDisplayEntryFee = "callDisplayEntryFee"
case _callUseFullCustomMessage = "callUseFullCustomMessage"
case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration"
case _bracketMatchFormatPreference = "bracketMatchFormatPreference"
case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference"
case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference"
}
} }

@ -32,6 +32,8 @@ class Club : ModelObject, Storable, Hashable {
var zipCode: String? var zipCode: String?
var latitude: Double? var latitude: Double?
var longitude: Double? var longitude: Double?
var courtCount: Int?
var courtNames: [String]? = nil
internal init(name: String, acronym: String? = nil, phone: String? = nil, code: String? = nil, address: String? = nil, city: String? = nil, zipCode: String? = nil, latitude: Double? = nil, longitude: Double? = nil) { internal init(name: String, acronym: String? = nil, phone: String? = nil, code: String? = nil, address: String? = nil, city: String? = nil, zipCode: String? = nil, latitude: Double? = nil, longitude: Double? = nil) {
self.name = name self.name = name
@ -45,12 +47,7 @@ class Club : ModelObject, Storable, Hashable {
self.longitude = longitude self.longitude = longitude
} }
var tournaments: [Tournament] {
return []
}
override func deleteDependencies() throws { override func deleteDependencies() throws {
try Store.main.deleteDependencies(items: self.tournaments)
} }
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
@ -64,6 +61,8 @@ class Club : ModelObject, Storable, Hashable {
case _zipCode = "zipCode" case _zipCode = "zipCode"
case _latitude = "latitude" case _latitude = "latitude"
case _longitude = "longitude" case _longitude = "longitude"
case _courtCount = "courtCount"
case _courtNames = "courtNames"
} }
} }

@ -24,8 +24,29 @@ class DataStore: ObservableObject {
fileprivate(set) var playerRegistrations: StoredCollection<PlayerRegistration> fileprivate(set) var playerRegistrations: StoredCollection<PlayerRegistration>
fileprivate(set) var rounds: StoredCollection<Round> fileprivate(set) var rounds: StoredCollection<Round>
fileprivate(set) var teamScores: StoredCollection<TeamScore> fileprivate(set) var teamScores: StoredCollection<TeamScore>
fileprivate(set) var monthData: StoredCollection<MonthData>
fileprivate var _userStorage: OptionalStorage<User> = OptionalStorage<User>(fileName: "user.json") fileprivate var _userStorage: OptionalStorage<User> = OptionalStorage<User>(fileName: "user.json")
fileprivate var _appSettingsStorage: MicroStorage<AppSettings> = MicroStorage()
var appSettings: AppSettings {
_appSettingsStorage.item
}
func updateSettings() {
_appSettingsStorage.update { settings in
settings.lastDataSource = appSettings.lastDataSource
settings.callMessageBody = appSettings.callMessageBody
settings.callDisplayFormat = appSettings.callDisplayFormat
settings.callMessageSignature = appSettings.callMessageSignature
settings.callDisplayEntryFee = appSettings.callDisplayEntryFee
settings.callUseFullCustomMessage = appSettings.callUseFullCustomMessage
settings.matchFormatsDefaultDuration = appSettings.matchFormatsDefaultDuration
settings.bracketMatchFormatPreference = appSettings.bracketMatchFormatPreference
settings.groupStageMatchFormatPreference = appSettings.groupStageMatchFormatPreference
settings.loserBracketMatchFormatPreference = appSettings.loserBracketMatchFormatPreference
}
}
var user: User? { var user: User? {
return self._userStorage.item return self._userStorage.item
@ -44,15 +65,17 @@ class DataStore: ObservableObject {
// store.addMigration(Migration<TournamentV1, TournamentV2>(version: 2)) // store.addMigration(Migration<TournamentV1, TournamentV2>(version: 2))
// store.addMigration(Migration<TournamentV2, Tournament>(version: 3)) // store.addMigration(Migration<TournamentV2, Tournament>(version: 3))
self.clubs = store.registerCollection(synchronized: false, indexed: true) let indexed : Bool = false
self.tournaments = store.registerCollection(synchronized: false, indexed: true) self.clubs = store.registerCollection(synchronized: false, indexed: indexed)
self.events = store.registerCollection(synchronized: false, indexed: true) self.tournaments = store.registerCollection(synchronized: false, indexed: indexed)
self.groupStages = store.registerCollection(synchronized: false, indexed: true) self.events = store.registerCollection(synchronized: false, indexed: indexed)
self.teamScores = store.registerCollection(synchronized: false, indexed: true) self.groupStages = store.registerCollection(synchronized: false, indexed: indexed)
self.teamRegistrations = store.registerCollection(synchronized: false, indexed: true) self.teamScores = store.registerCollection(synchronized: false, indexed: indexed)
self.playerRegistrations = store.registerCollection(synchronized: false, indexed: true) self.teamRegistrations = store.registerCollection(synchronized: false, indexed: indexed)
self.rounds = store.registerCollection(synchronized: false, indexed: true) self.playerRegistrations = store.registerCollection(synchronized: false, indexed: indexed)
self.matches = store.registerCollection(synchronized: false, indexed: true) self.rounds = store.registerCollection(synchronized: false, indexed: indexed)
self.matches = store.registerCollection(synchronized: false, indexed: indexed)
self.monthData = store.registerCollection(synchronized: false, indexed: indexed)
NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidLoad, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidLoad, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidChange, object: nil)

@ -22,6 +22,7 @@ class Event: ModelObject, Storable {
var groupStageFormat: Int? var groupStageFormat: Int?
var roundFormat: Int? var roundFormat: Int?
var loserRoundFormat: Int? var loserRoundFormat: Int?
//var timeslots ?
internal init(club: String? = nil, name: String? = nil, courtCount: Int? = nil, tenupId: String? = nil, groupStageFormat: Int? = nil, roundFormat: Int? = nil, loserRoundFormat: Int? = nil) { internal init(club: String? = nil, name: String? = nil, courtCount: Int? = nil, tenupId: String? = nil, groupStageFormat: Int? = nil, roundFormat: Int? = nil, loserRoundFormat: Int? = nil) {
self.club = club self.club = club

@ -203,6 +203,7 @@ enum TypePratique: String, Codable {
case beach = "BEACH" case beach = "BEACH"
case padel = "PADEL" case padel = "PADEL"
case tennis = "TENNIS" case tennis = "TENNIS"
case pickle = "PICKLE"
} }
// MARK: - CategorieTournoi // MARK: - CategorieTournoi

@ -19,6 +19,7 @@ class GroupStage: ModelObject, Storable {
var size: Int var size: Int
var format: Int? var format: Int?
var startDate: Date? var startDate: Date?
var name: String?
var matchFormat: MatchFormat { var matchFormat: MatchFormat {
get { get {
@ -256,6 +257,15 @@ class GroupStage: ModelObject, Storable {
} }
} }
func updateMatchFormat(_ matchFormat: MatchFormat) {
self.matchFormat = matchFormat
let playedMatches = playedMatches()
playedMatches.forEach { match in
match.matchFormat = matchFormat
}
try? DataStore.shared.matches.addOrUpdate(contentOfs: playedMatches)
}
override func deleteDependencies() throws { override func deleteDependencies() throws {
try Store.main.deleteDependencies(items: self._matches()) try Store.main.deleteDependencies(items: self._matches())
} }
@ -269,6 +279,7 @@ extension GroupStage {
case _size = "size" case _size = "size"
case _format = "format" case _format = "format"
case _startDate = "startDate" case _startDate = "startDate"
case _name = "name"
} }
} }

@ -27,6 +27,7 @@ class Match: ModelObject, Storable {
var name: String? var name: String?
var order: Int var order: Int
var disabled: Bool = false var disabled: Bool = false
var courtIndex: Int?
internal init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, court: String? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, broadcasted: Bool = false, name: String? = nil, order: Int = 0) { internal init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, court: String? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, broadcasted: Bool = false, name: String? = nil, order: Int = 0) {
self.round = round self.round = round
@ -100,8 +101,8 @@ class Match: ModelObject, Storable {
return index * 2 + teamPosition.rawValue == bracketPosition return index * 2 + teamPosition.rawValue == bracketPosition
} }
func estimatedEndDate() -> Date? { func estimatedEndDate(_ additionalEstimationDuration: Int) -> Date? {
let minutesToAdd = Double(matchFormat.estimatedDuration) let minutesToAdd = Double(matchFormat.getEstimatedDuration(additionalEstimationDuration))
return startDate?.addingTimeInterval(minutesToAdd * 60.0) return startDate?.addingTimeInterval(minutesToAdd * 60.0)
} }
@ -315,7 +316,7 @@ class Match: ModelObject, Storable {
} }
} }
func courtIndex() -> Int? { func getCourtIndex() -> Int? {
guard let court else { return nil } guard let court else { return nil }
if let courtIndex = Int(court) { return courtIndex - 1 } if let courtIndex = Int(court) { return courtIndex - 1 }
return nil return nil
@ -536,6 +537,7 @@ class Match: ModelObject, Storable {
case _index = "index" case _index = "index"
case _format = "format" case _format = "format"
case _court = "court" case _court = "court"
case _courtIndex = "courtIndex"
case _servingTeamId = "servingTeamId" case _servingTeamId = "servingTeamId"
case _winningTeamId = "winningTeamId" case _winningTeamId = "winningTeamId"
case _losingTeamId = "losingTeamId" case _losingTeamId = "losingTeamId"

@ -35,17 +35,12 @@ extension Tournament {
} }
static func newEmptyInstance() -> Tournament { static func newEmptyInstance() -> Tournament {
let lastDataSource: String? = UserDefaults.standard.string(forKey: "lastDataSource") let lastDataSource: String? = DataStore.shared.appSettings.lastDataSource
let lastDataSourceMaleUnranked: Int = UserDefaults.standard.integer(forKey: "lastDataSourceMaleUnranked")
let lastDataSourceFemaleUnranked: Int = UserDefaults.standard.integer(forKey: "lastDataSourceFemaleUnranked")
var _mostRecentDateAvailable: Date? { var _mostRecentDateAvailable: Date? {
guard let lastDataSource else { return nil } guard let lastDataSource else { return nil }
return URL.importDateFormatter.date(from: lastDataSource) return URL.importDateFormatter.date(from: lastDataSource)
} }
let maleUnrankedValue : Int? = lastDataSourceMaleUnranked == 0 ? nil : lastDataSourceMaleUnranked
let femaleUnrankedValue : Int? = lastDataSourceFemaleUnranked == 0 ? nil : lastDataSourceMaleUnranked
let rankSourceDate = _mostRecentDateAvailable let rankSourceDate = _mostRecentDateAvailable
//todo //todo
@ -55,7 +50,7 @@ extension Tournament {
tournament.federalTournamentAge = FederalTournamentAge.mostUsed(tournaments: tournaments) tournament.federalTournamentAge = FederalTournamentAge.mostUsed(tournaments: tournaments)
*/ */
return Tournament(groupStageSortMode: .snake, rankSourceDate: rankSourceDate, teamSorting: .inscriptionDate, federalCategory: .men, federalLevelCategory: .p100, federalAgeCategory: .senior, maleUnrankedValue: maleUnrankedValue, femaleUnrankedValue: femaleUnrankedValue) return Tournament(groupStageSortMode: .snake, rankSourceDate: rankSourceDate, teamSorting: .inscriptionDate, federalCategory: .men, federalLevelCategory: .p100, federalAgeCategory: .senior)
} }
} }

@ -0,0 +1,53 @@
//
// MonthData.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import Foundation
import SwiftUI
import LeStorage
@Observable
class MonthData : ModelObject, Storable {
static func resourceName() -> String { return "month-data" }
private(set) var id: String = Store.randomId()
private(set) var monthKey: String
private(set) var creationDate: Date
var maleUnrankedValue: Int? = nil
var femaleUnrankedValue: Int? = nil
init(monthKey: String) {
self.monthKey = monthKey
self.creationDate = Date()
}
static func calculateCurrentUnrankedValues(mostRecentDateAvailable: Date) async {
let lastDataSourceMaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: mostRecentDateAvailable, man: true)
let lastDataSourceFemaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: mostRecentDateAvailable, man: false)
await MainActor.run {
if let lastDataSource = DataStore.shared.appSettings.lastDataSource {
let currentMonthData : MonthData = Store.main.filter(isIncluded: { $0.monthKey == lastDataSource }).first ?? MonthData(monthKey: lastDataSource)
currentMonthData.maleUnrankedValue = lastDataSourceMaleUnranked
currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked
try? DataStore.shared.monthData.addOrUpdate(instance: currentMonthData)
}
}
}
override func deleteDependencies() throws {
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _monthKey = "monthKey"
case _creationDate = "creationDate"
case _maleUnrankedValue = "maleUnrankedValue"
case _femaleUnrankedValue = "femaleUnrankedValue"
}
}

@ -387,6 +387,15 @@ class Round: ModelObject, Storable {
return Store.main.findById(parentRound) return Store.main.findById(parentRound)
} }
func updateMatchFormat(_ matchFormat: MatchFormat) {
self.matchFormat = matchFormat
let playedMatches = _matches()
playedMatches.forEach { match in
match.matchFormat = matchFormat
}
try? DataStore.shared.matches.addOrUpdate(contentOfs: playedMatches)
}
override func deleteDependencies() throws { override func deleteDependencies() throws {
try Store.main.deleteDependencies(items: _matches()) try Store.main.deleteDependencies(items: _matches())
try Store.main.deleteDependencies(items: loserRoundsAndChildren()) try Store.main.deleteDependencies(items: loserRoundsAndChildren())

@ -41,14 +41,13 @@ class Tournament : ModelObject, Storable {
var qualifiedPerGroupStage: Int var qualifiedPerGroupStage: Int
var teamsPerGroupStage: Int var teamsPerGroupStage: Int
var entryFee: Double? var entryFee: Double?
var maleUnrankedValue: Int?
var femaleUnrankedValue: Int?
var payment: TournamentPayment = .free var payment: TournamentPayment = .free
var additionalEstimationDuration: Int = 0
@ObservationIgnored @ObservationIgnored
var navigationPath: [Screen] = [] var navigationPath: [Screen] = []
internal init(event: String? = nil, creator: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = true, groupStageFormat: Int? = nil, roundFormat: Int? = nil, loserRoundFormat: Int? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, groupStageCourtCount: Int? = nil, seedCount: Int = 8, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, maleUnrankedValue: Int? = nil, femaleUnrankedValue: Int? = nil) { internal init(event: String? = nil, creator: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = true, groupStageFormat: Int? = nil, roundFormat: Int? = nil, loserRoundFormat: Int? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, groupStageCourtCount: Int? = nil, seedCount: Int = 8, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil) {
self.event = event self.event = event
self.creator = creator self.creator = creator
self.name = name self.name = name
@ -77,8 +76,6 @@ class Tournament : ModelObject, Storable {
self.qualifiedPerGroupStage = qualifiedPerGroupStage self.qualifiedPerGroupStage = qualifiedPerGroupStage
self.teamsPerGroupStage = teamsPerGroupStage self.teamsPerGroupStage = teamsPerGroupStage
self.entryFee = entryFee self.entryFee = entryFee
self.maleUnrankedValue = maleUnrankedValue
self.femaleUnrankedValue = femaleUnrankedValue
self.teamSorting = teamSorting ?? federalLevelCategory.defaultTeamSortingType self.teamSorting = teamSorting ?? federalLevelCategory.defaultTeamSortingType
} }
@ -386,7 +383,7 @@ class Tournament : ModelObject, Storable {
_sortedTeams = bracketTeams.sorted(using: _currentSelectionSorting, order: .ascending) + groupStageTeams.sorted(using: _currentSelectionSorting, order: .ascending) _sortedTeams = bracketTeams.sorted(using: _currentSelectionSorting, order: .ascending) + groupStageTeams.sorted(using: _currentSelectionSorting, order: .ascending)
} }
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) //let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
//print("func selectedSortedTeams", id, tournamentTitle(), duration.formatted(.units(allowed: [.seconds, .milliseconds]))) //print("func selectedSortedTeams", id, tournamentTitle(), duration.formatted(.units(allowed: [.seconds, .milliseconds])))
return _sortedTeams return _sortedTeams
} }
@ -609,9 +606,19 @@ class Tournament : ModelObject, Storable {
guard let newDate else { return } guard let newDate else { return }
rankSourceDate = newDate rankSourceDate = newDate
if currentMonthData() == nil {
let lastRankWoman = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: rankSourceDate) let lastRankWoman = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: rankSourceDate)
let lastRankMan = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: rankSourceDate) let lastRankMan = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: rankSourceDate)
await MainActor.run {
let monthData = MonthData(monthKey: URL.importDateFormatter.string(from: newDate))
monthData.maleUnrankedValue = lastRankMan
monthData.femaleUnrankedValue = lastRankWoman
try? DataStore.shared.monthData.addOrUpdate(instance: monthData)
}
}
let lastRankMan = currentMonthData()?.maleUnrankedValue
let lastRankWoman = currentMonthData()?.femaleUnrankedValue
try await unsortedPlayers().concurrentForEach { player in try await unsortedPlayers().concurrentForEach { player in
let dataURLs = SourceFileManager.shared.allFiles.filter({ $0.dateFromPath == newDate }) let dataURLs = SourceFileManager.shared.allFiles.filter({ $0.dateFromPath == newDate })
@ -619,11 +626,6 @@ class Tournament : ModelObject, Storable {
try await player.updateRank(from: sources, lastRank: (player.sex == 0 ? lastRankWoman : lastRankMan) ?? 0) try await player.updateRank(from: sources, lastRank: (player.sex == 0 ? lastRankWoman : lastRankMan) ?? 0)
} }
await MainActor.run {
self.maleUnrankedValue = lastRankMan
self.femaleUnrankedValue = lastRankWoman
}
} }
func missingUnrankedValue() -> Bool { func missingUnrankedValue() -> Bool {
@ -1035,6 +1037,21 @@ class Tournament : ModelObject, Storable {
try Store.main.deleteDependencies(items: self.groupStages()) try Store.main.deleteDependencies(items: self.groupStages())
try Store.main.deleteDependencies(items: self.rounds()) try Store.main.deleteDependencies(items: self.rounds())
} }
func currentMonthData() -> MonthData? {
guard let rankSourceDate else { return nil }
let dateString = URL.importDateFormatter.string(from: rankSourceDate)
return Store.main.filter(isIncluded: { $0.monthKey == dateString }).first
}
var maleUnrankedValue: Int? {
currentMonthData()?.maleUnrankedValue
}
var femaleUnrankedValue: Int? {
currentMonthData()?.femaleUnrankedValue
}
} }
extension Tournament { extension Tournament {
@ -1068,8 +1085,7 @@ extension Tournament {
case _qualifiedPerGroupStage = "qualifiedPerGroupStage" case _qualifiedPerGroupStage = "qualifiedPerGroupStage"
case _teamsPerGroupStage = "teamsPerGroupStage" case _teamsPerGroupStage = "teamsPerGroupStage"
case _entryFee = "entryFee" case _entryFee = "entryFee"
case _maleUnrankedValue = "maleUnrankedValue" case _additionalEstimationDuration = "additionalEstimationDuration"
case _femaleUnrankedValue = "femaleUnrankedValue"
} }
} }

@ -27,11 +27,6 @@ class User: UserBase {
var lastName: String var lastName: String
var phone: String? var phone: String?
var country: String? var country: String?
var callMessageBody : String? = nil
var callMessageSignature: String? = nil
var callDisplayFormat: Bool = false
var callDisplayEntryFee: Bool = false
var callUseFullCustomMessage: Bool = false
init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?) { init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?) {
self.username = username self.username = username
@ -69,12 +64,6 @@ class User: UserBase {
case _lastName = "lastName" case _lastName = "lastName"
case _phone = "phone" case _phone = "phone"
case _country = "country" case _country = "country"
case _callMessageBody = "callMessageBody"
case _callMessageSignature = "callMessageSignature"
case _callDisplayFormat = "callDisplayFormat"
case _callDisplayEntryFee = "callDisplayEntryFee"
case _callUseFullCustomMessage = "callUseFullCustomMessage"
} }
} }

@ -34,7 +34,7 @@ extension ContactType {
static let defaultSignature = "" static let defaultSignature = ""
static func callingGroupStageCustomMessage(tournament: Tournament?, startDate: Date?, roundLabel: String) -> String { static func callingGroupStageCustomMessage(tournament: Tournament?, startDate: Date?, roundLabel: String) -> String {
let tournamentCustomMessage = DataStore.shared.user?.callMessageBody ?? defaultCustomMessage let tournamentCustomMessage = DataStore.shared.appSettings.callMessageBody ?? defaultCustomMessage
let clubName = tournament?.clubName ?? "" let clubName = tournament?.clubName ?? ""
var text = tournamentCustomMessage var text = tournamentCustomMessage
@ -49,7 +49,7 @@ extension ContactType {
text = text.replacingOccurrences(of: "#jour", with: "\(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide)))") text = text.replacingOccurrences(of: "#jour", with: "\(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide)))")
text = text.replacingOccurrences(of: "#horaire", with: "\(date.formatted(Date.FormatStyle().hour().minute()))") text = text.replacingOccurrences(of: "#horaire", with: "\(date.formatted(Date.FormatStyle().hour().minute()))")
let signature = DataStore.shared.user?.callMessageSignature ?? defaultSignature let signature = DataStore.shared.appSettings.callMessageSignature ?? defaultSignature
text = text.replacingOccurrences(of: "#signature", with: signature) text = text.replacingOccurrences(of: "#signature", with: signature)
return text return text
@ -57,7 +57,7 @@ extension ContactType {
static func callingGroupStageMessage(tournament: Tournament?, startDate: Date?, roundLabel: String, matchFormat: MatchFormat?) -> String { static func callingGroupStageMessage(tournament: Tournament?, startDate: Date?, roundLabel: String, matchFormat: MatchFormat?) -> String {
let useFullCustomMessage = DataStore.shared.user?.callUseFullCustomMessage ?? false let useFullCustomMessage = DataStore.shared.appSettings.callUseFullCustomMessage ?? false
if useFullCustomMessage { if useFullCustomMessage {
return callingGroupStageCustomMessage(tournament: tournament, startDate: startDate, roundLabel: roundLabel) return callingGroupStageCustomMessage(tournament: tournament, startDate: startDate, roundLabel: roundLabel)
@ -66,17 +66,17 @@ extension ContactType {
let date = startDate ?? tournament?.startDate ?? Date() let date = startDate ?? tournament?.startDate ?? Date()
let clubName = tournament?.clubName ?? "" let clubName = tournament?.clubName ?? ""
let message = DataStore.shared.user?.callMessageBody ?? defaultCustomMessage let message = DataStore.shared.appSettings.callMessageBody ?? defaultCustomMessage
let signature = DataStore.shared.user?.callMessageSignature ?? defaultSignature let signature = DataStore.shared.appSettings.callMessageSignature ?? defaultSignature
let localizedCalled = "convoqué" + (tournament?.tournamentCategory == .women ? "e" : "") + "s" let localizedCalled = "convoqué" + (tournament?.tournamentCategory == .women ? "e" : "") + "s"
var formatMessage: String? { var formatMessage: String? {
(DataStore.shared.user?.callDisplayFormat ?? false) ? matchFormat?.computedLongLabel.appending(".") : nil (DataStore.shared.appSettings.callDisplayFormat ?? false) ? matchFormat?.computedLongLabel.appending(".") : nil
} }
var entryFeeMessage: String? { var entryFeeMessage: String? {
(DataStore.shared.user?.callDisplayEntryFee ?? false) ? tournament?.entryFeeMessage : nil (DataStore.shared.appSettings.callDisplayEntryFee ?? false) ? tournament?.entryFeeMessage : nil
} }
var computedMessage: String { var computedMessage: String {

@ -1046,16 +1046,16 @@ enum MatchFormat: Int, Hashable, Codable, CaseIterable {
} }
} }
var estimatedDuration: Int { func getEstimatedDuration(_ additionalDuration: Int = 0) -> Int {
if UserDefaults.standard.object(forKey: format) != nil { estimatedDuration + additionalDuration
return UserDefaults.standard.integer(forKey: format)
} else {
return defaultEstimatedDuration
} }
private var estimatedDuration: Int {
DataStore.shared.appSettings.matchFormatsDefaultDuration?[self] ?? defaultEstimatedDuration
} }
func formattedEstimatedDuration() -> String { func formattedEstimatedDuration(_ additionalDuration: Int = 0) -> String {
Duration.seconds(estimatedDuration * 60).formatted(.units(allowed: [.minutes])) Duration.seconds((estimatedDuration + additionalDuration) * 60).formatted(.units(allowed: [.minutes]))
} }
func formattedEstimatedBreakDuration() -> String { func formattedEstimatedBreakDuration() -> String {

@ -12,7 +12,7 @@ class SourceFileManager {
static let beachPadel = URL(string: "https://beach-padel.app.fft.fr/beachja/index/")! static let beachPadel = URL(string: "https://beach-padel.app.fft.fr/beachja/index/")!
var lastDataSource: String? { var lastDataSource: String? {
UserDefaults.standard.string(forKey: "lastDataSource") DataStore.shared.appSettings.lastDataSource
} }
func lastDataSourceDate() -> Date? { func lastDataSourceDate() -> Date? {

@ -0,0 +1,13 @@
//
// AppScreen.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import Foundation
enum AppScreen: CaseIterable, Identifiable {
var id: Self { self }
case matchFormatSettings
}

@ -62,6 +62,7 @@ enum MatchSchedulerOption: Hashable {
class MatchScheduler { class MatchScheduler {
static let shared = MatchScheduler() static let shared = MatchScheduler()
var additionalEstimationDuration : Int = 0
var options: Set<MatchSchedulerOption> = Set(arrayLiteral: .accountUpperBracketBreakTime) var options: Set<MatchSchedulerOption> = Set(arrayLiteral: .accountUpperBracketBreakTime)
var timeDifferenceLimit: Double = 300.0 var timeDifferenceLimit: Double = 300.0
var loserBracketRotationDifference: Int = 0 var loserBracketRotationDifference: Int = 0
@ -254,7 +255,7 @@ class MatchScheduler {
let matchesByCourt = byCourt[court]?.sorted(by: \.startDate!) let matchesByCourt = byCourt[court]?.sorted(by: \.startDate!)
let lastMatch = matchesByCourt?.last let lastMatch = matchesByCourt?.last
var results = [(String, Date)]() var results = [(String, Date)]()
if let courtFreeDate = lastMatch?.estimatedEndDate() { if let courtFreeDate = lastMatch?.estimatedEndDate(additionalEstimationDuration) {
results.append((court, courtFreeDate)) results.append((court, courtFreeDate))
} }
return results return results
@ -276,7 +277,8 @@ class MatchScheduler {
_startDate = match.startDate _startDate = match.startDate
rotationIndex += 1 rotationIndex += 1
} }
let timeMatch = TimeMatch(matchID: match.id, rotationIndex: rotationIndex, courtIndex: match.courtIndex() ?? 0, startDate: match.startDate!, durationLeft: match.matchFormat.estimatedDuration, minimumBreakTime: match.matchFormat.breakTime.breakTime)
let timeMatch = TimeMatch(matchID: match.id, rotationIndex: rotationIndex, courtIndex: match.getCourtIndex() ?? 0, startDate: match.startDate!, durationLeft: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), minimumBreakTime: match.matchFormat.breakTime.breakTime)
slots.append(timeMatch) slots.append(timeMatch)
} }
@ -398,7 +400,7 @@ class MatchScheduler {
matchPerRound[first.roundObject!.index] = 1 matchPerRound[first.roundObject!.index] = 1
} }
} }
let timeMatch = TimeMatch(matchID: first.id, rotationIndex: rotationIndex, courtIndex: courtIndex, startDate: rotationStartDate, durationLeft: first.matchFormat.estimatedDuration, minimumBreakTime: first.matchFormat.breakTime.breakTime) let timeMatch = TimeMatch(matchID: first.id, rotationIndex: rotationIndex, courtIndex: courtIndex, startDate: rotationStartDate, durationLeft: first.matchFormat.getEstimatedDuration(additionalEstimationDuration), minimumBreakTime: first.matchFormat.breakTime.breakTime)
slots.append(timeMatch) slots.append(timeMatch)
availableMatchs.removeAll(where: { $0.id == first.id }) availableMatchs.removeAll(where: { $0.id == first.id })
} else { } else {

@ -10,6 +10,7 @@ import SwiftUI
@Observable @Observable
class NavigationViewModel { class NavigationViewModel {
var path = NavigationPath() var path = NavigationPath()
var selectedTab: TabDestination?
var agendaDestination: AgendaDestination? = .activity var agendaDestination: AgendaDestination? = .activity
var tournament: Tournament? var tournament: Tournament?
} }

@ -10,18 +10,16 @@ import SwiftUI
struct CallMessageCustomizationView: View { struct CallMessageCustomizationView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
var tournament: Tournament var tournament: Tournament
var user: User
@FocusState private var textEditor: Bool @FocusState private var textEditor: Bool
@State private var customClubName: String = "" @State private var customClubName: String = ""
@State private var customCallMessageBody: String = "" @State private var customCallMessageBody: String = ""
@State private var customCallMessageSignature: String = "" @State private var customCallMessageSignature: String = ""
init(tournament: Tournament, user: User) { init(tournament: Tournament) {
self.tournament = tournament self.tournament = tournament
self.user = user _customCallMessageBody = State(wrappedValue: DataStore.shared.appSettings.callMessageBody ?? "")
_customCallMessageBody = State(wrappedValue: user.callMessageBody ?? "") _customCallMessageSignature = State(wrappedValue: DataStore.shared.appSettings.callMessageSignature ?? "")
_customCallMessageSignature = State(wrappedValue: user.callMessageSignature ?? "")
_customClubName = State(wrappedValue: tournament.clubName ?? "") _customClubName = State(wrappedValue: tournament.clubName ?? "")
} }
@ -30,11 +28,11 @@ struct CallMessageCustomizationView: View {
} }
var formatMessage: String? { var formatMessage: String? {
user.callDisplayFormat ? tournament.matchFormat.computedLongLabel + "." : nil dataStore.appSettings.callDisplayFormat ? tournament.matchFormat.computedLongLabel + "." : nil
} }
var entryFeeMessage: String? { var entryFeeMessage: String? {
user.callDisplayEntryFee ? tournament.entryFeeMessage : nil dataStore.appSettings.callDisplayEntryFee ? tournament.entryFeeMessage : nil
} }
var computedMessage: String { var computedMessage: String {
@ -47,7 +45,7 @@ struct CallMessageCustomizationView: View {
} }
var body: some View { var body: some View {
@Bindable var user = user @Bindable var appSettings = dataStore.appSettings
List { List {
Section { Section {
ZStack { ZStack {
@ -86,7 +84,7 @@ struct CallMessageCustomizationView: View {
} }
Section { Section {
if user.callUseFullCustomMessage { if appSettings.callUseFullCustomMessage {
Text(self.computedFullCustomMessage()) Text(self.computedFullCustomMessage())
.contextMenu { .contextMenu {
Button("Coller dans le presse-papier") { Button("Coller dans le presse-papier") {
@ -108,7 +106,7 @@ struct CallMessageCustomizationView: View {
Section { Section {
LabeledContent { LabeledContent {
Toggle(isOn: $user.callUseFullCustomMessage) { Toggle(isOn: $appSettings.callUseFullCustomMessage) {
} }
} label: { } label: {
@ -124,13 +122,13 @@ struct CallMessageCustomizationView: View {
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
Menu { Menu {
Picker(selection: $user.callDisplayFormat) { Picker(selection: $appSettings.callDisplayFormat) {
Text("Afficher le format").tag(true) Text("Afficher le format").tag(true)
Text("Masquer le format").tag(false) Text("Masquer le format").tag(false)
} label: { } label: {
} }
Picker(selection: $user.callDisplayEntryFee) { Picker(selection: $appSettings.callDisplayEntryFee) {
Text("Afficher le prix d'inscription").tag(true) Text("Afficher le prix d'inscription").tag(true)
Text("Masquer le prix d'inscription").tag(false) Text("Masquer le prix d'inscription").tag(false)
} label: { } label: {
@ -151,30 +149,30 @@ struct CallMessageCustomizationView: View {
} }
} }
} }
.onChange(of: user.callUseFullCustomMessage) { .onChange(of: appSettings.callUseFullCustomMessage) {
if user.callUseFullCustomMessage == false { if appSettings.callUseFullCustomMessage == false {
user.callMessageBody = ContactType.defaultCustomMessage appSettings.callMessageBody = ContactType.defaultCustomMessage
} }
_save() _save()
} }
.onChange(of: customCallMessageBody) { .onChange(of: customCallMessageBody) {
user.callMessageBody = customCallMessageBody appSettings.callMessageBody = customCallMessageBody
_save() _save()
} }
.onChange(of: customCallMessageSignature) { .onChange(of: customCallMessageSignature) {
user.callMessageSignature = customCallMessageSignature appSettings.callMessageSignature = customCallMessageSignature
_save() _save()
} }
.onChange(of: user.callDisplayEntryFee) { .onChange(of: appSettings.callDisplayEntryFee) {
_save() _save()
} }
.onChange(of: user.callDisplayFormat) { .onChange(of: appSettings.callDisplayFormat) {
_save() _save()
} }
} }
private func _save() { private func _save() {
try? dataStore.setUser(user) dataStore.updateSettings()
} }
func computedFullCustomMessage() -> String { func computedFullCustomMessage() -> String {

@ -14,15 +14,13 @@ struct CallSettingsView: View {
var body: some View { var body: some View {
List { List {
if let user = dataStore.user {
Section { Section {
NavigationLink { NavigationLink {
CallMessageCustomizationView(tournament: tournament, user: user) CallMessageCustomizationView(tournament: tournament)
} label: { } label: {
Text("Personnaliser le message de convocation") Text("Personnaliser le message de convocation")
} }
} }
}
Section { Section {
RowButtonView("Envoyer un message à tout le monde") { RowButtonView("Envoyer un message à tout le monde") {

@ -339,6 +339,7 @@ enum Pratique: String, Codable {
case beach = "BEACH" case beach = "BEACH"
case padel = "PADEL" case padel = "PADEL"
case tennis = "TENNIS" case tennis = "TENNIS"
case pickle = "PICKLE"
} }
// MARK: - ClubMarker // MARK: - ClubMarker

@ -1,23 +0,0 @@
//
// ClubView.swift
// PadelClub
//
// Created by Laurent Morvillier on 06/02/2024.
//
import SwiftUI
struct ClubView: View {
var club: Club
var body: some View {
List(club.tournaments) { tournament in
Text(tournament.tournamentTitle())
}.navigationTitle(club.name)
}
}
#Preview {
ClubView(club: Club(name: "AUC", acronym: "test", address: ""))
}

@ -15,13 +15,13 @@ struct LabelOptions: View {
struct LabelStructure: View { struct LabelStructure: View {
var body: some View { var body: some View {
Label("Structure", systemImage: "hammer") Label("Structure", systemImage: "hammer").labelStyle(.titleOnly)
} }
} }
struct LabelSettings: View { struct LabelSettings: View {
var body: some View { var body: some View {
Label("Réglages", systemImage: "slider.horizontal.3") Label("Réglages", systemImage: "slider.horizontal.3").labelStyle(.titleOnly)
} }
} }

@ -13,12 +13,12 @@ struct StepperView: View {
var title: String? = nil var title: String? = nil
@Binding var count: Int @Binding var count: Int
var step: Int = 1
var minimum: Int? = nil var minimum: Int? = nil
var maximum: Int? = nil var maximum: Int? = nil
var body: some View { var body: some View {
VStack(spacing: 0) { VStack {
HStack(spacing: 8) { HStack(spacing: 8) {
Button(action: { Button(action: {
self._subtract() self._subtract()
@ -74,14 +74,14 @@ struct StepperView: View {
if let maximum, self.count + 1 > maximum { if let maximum, self.count + 1 > maximum {
return return
} }
self.count += 1 self.count += step
} }
fileprivate func _subtract() { fileprivate func _subtract() {
if let minimum, self.count - 1 < minimum { if let minimum, self.count - 1 < minimum {
return return
} }
self.count -= 1 self.count -= step
} }
} }

@ -1,82 +0,0 @@
//
// ContentView.swift
// PadelClub
//
// Created by Laurent Morvillier on 02/02/2024.
//
import SwiftUI
import LeStorage
struct ContentView: View {
@StateObject var dataStore = DataStore()
var body: some View {
NavigationStack {
VStack {
List(self.dataStore.clubs) { club in
NavigationLink {
ClubView(club: club)
} label: {
Text(club.name)
}
}
Button("add") {
self._add()
}
.padding()
.buttonStyle(.bordered)
}
.toolbar(content: {
ToolbarItem {
NavigationLink {
MainUserView()
.environmentObject(self.dataStore)
} label: {
Image(systemName: "person.circle.fill")
}
}
ToolbarItem {
NavigationLink {
SubscriptionView()
} label: {
Image(systemName: "tennisball.circle.fill")
}
}
})
.navigationTitle("Home")
}
}
func _add() {
// let id = (0...1000000).randomElement()!
// let club: Club = Club(name: "test\(id)", address: "some address")
// self.dataStore.clubs.addOrUpdate(instance: club)
// for _ in 0...20 {
// var clubs: [Club] = []
// for _ in 0...20 {
// let id = (0...1000000).randomElement()!
// let club: Club = Club(name: "test\(id)", acronym: "test", address: "some address")
// clubs.append(club)
// }
// do {
// try self.dataStore.clubs.append(contentOfs: clubs)
// } catch {
// Logger.error(error)
// }
// }
}
}
#Preview {
ContentView()
}

@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
struct MatchDateView: View { struct MatchDateView: View {
@Environment(Tournament.self) var tournament: Tournament
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
var match: Match var match: Match
var showPrefix: Bool = false var showPrefix: Bool = false
@ -31,8 +32,9 @@ struct MatchDateView: View {
save() save()
} }
} else { } else {
Button("Décaler de \(match.matchFormat.estimatedDuration) minutes") { let estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)
match.startDate = match.startDate?.addingTimeInterval(Double(match.matchFormat.estimatedDuration) * 60.0) Button("Décaler de \(estimatedDuration) minutes") {
match.startDate = match.startDate?.addingTimeInterval(Double(estimatedDuration) * 60.0)
match.endDate = nil match.endDate = nil
save() save()
} }

@ -9,6 +9,7 @@ import SwiftUI
struct MatchDetailView: View { struct MatchDetailView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
let matchViewStyle: MatchViewStyle let matchViewStyle: MatchViewStyle
@ -320,14 +321,15 @@ struct MatchDetailView: View {
Section { Section {
if match.hasEnded() == false { if match.hasEnded() == false {
let rotationDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)
Picker(selection: $startDateSetup) { Picker(selection: $startDateSetup) {
if match.isReady() { if match.isReady() {
Text("Dans 5 minutes").tag(MatchDateSetup.inMinutes(5)) Text("Dans 5 minutes").tag(MatchDateSetup.inMinutes(5))
Text("Dans 15 minutes").tag(MatchDateSetup.inMinutes(15)) Text("Dans 15 minutes").tag(MatchDateSetup.inMinutes(15))
Text("Tout de suite").tag(MatchDateSetup.now) Text("Tout de suite").tag(MatchDateSetup.now)
} }
Text("Précédente rotation").tag(MatchDateSetup.inMinutes(-match.matchFormat.estimatedDuration)) Text("Précédente rotation").tag(MatchDateSetup.inMinutes(-rotationDuration))
Text("Prochaine rotation").tag(MatchDateSetup.inMinutes(match.matchFormat.estimatedDuration)) Text("Prochaine rotation").tag(MatchDateSetup.inMinutes(rotationDuration))
Text("À").tag(MatchDateSetup.customDate) Text("À").tag(MatchDateSetup.customDate)
} label: { } label: {
Text("Horaire") Text("Horaire")

@ -19,7 +19,6 @@ struct ActivityView: View {
@State private var viewStyle: AgendaDestination.ViewStyle = .list @State private var viewStyle: AgendaDestination.ViewStyle = .list
@State private var federalTournaments: [FederalTournament] = [] @State private var federalTournaments: [FederalTournament] = []
@State private var isGatheringFederalTournaments: Bool = false @State private var isGatheringFederalTournaments: Bool = false
@Binding var selectedTab: TabDestination?
@State private var error: Error? @State private var error: Error?
var runningTournaments: [FederalTournamentHolder] { var runningTournaments: [FederalTournamentHolder] {
@ -257,7 +256,7 @@ struct ActivityView: View {
Text("Pour voir vos tournois tenup ici, indiquez vos clubs préférés.") Text("Pour voir vos tournois tenup ici, indiquez vos clubs préférés.")
} actions: { } actions: {
RowButtonView("Choisir mes clubs préférés") { RowButtonView("Choisir mes clubs préférés") {
selectedTab = .umpire navigation.selectedTab = .umpire
} }
} }
} else { } else {
@ -276,6 +275,5 @@ struct ActivityView: View {
} }
#Preview { #Preview {
ActivityView(selectedTab: .constant(.activity)) ActivityView()
.environmentObject(DataStore.shared)
} }

@ -6,17 +6,19 @@
// //
import SwiftUI import SwiftUI
import LeStorage
struct MainView: View { struct MainView: View {
@StateObject var dataStore = DataStore.shared @StateObject var dataStore = DataStore.shared
@AppStorage("importingFiles") var importingFiles: Bool = false @AppStorage("importingFiles") var importingFiles: Bool = false
@Environment(NavigationViewModel.self) private var navigation: NavigationViewModel
@State private var checkingFilesAttempt: Int = 0 @State private var checkingFilesAttempt: Int = 0
@State private var checkingFiles: Bool = false @State private var checkingFiles: Bool = false
@AppStorage("lastDataSource") var lastDataSource: String? var lastDataSource: String? {
@AppStorage("lastDataSourceMaleUnranked") var lastDataSourceMaleUnranked: Int? dataStore.appSettings.lastDataSource
@AppStorage("lastDataSourceFemaleUnranked") var lastDataSourceFemaleUnranked: Int? }
@Environment(\.managedObjectContext) private var viewContext @Environment(\.managedObjectContext) private var viewContext
@ -25,10 +27,10 @@ struct MainView: View {
animation: .default) animation: .default)
private var players: FetchedResults<ImportedPlayer> private var players: FetchedResults<ImportedPlayer>
@State private var selectedTab: TabDestination?
var body: some View { var body: some View {
TabView(selection: $selectedTab) { @Bindable var navigation = navigation
ActivityView(selectedTab: $selectedTab) TabView(selection: $navigation.selectedTab) {
ActivityView()
.tabItem(for: .activity) .tabItem(for: .activity)
TournamentOrganizerView() TournamentOrganizerView()
.tabItem(for: .tournamentOrganizer) .tabItem(for: .tournamentOrganizer)
@ -87,7 +89,7 @@ struct MainView: View {
} }
private func _checkSourceFileAvailability() async { private func _checkSourceFileAvailability() async {
print(dataStore.appSettings.lastDataSource)
print("check internet") print("check internet")
print("check files on internet") print("check files on internet")
print("check if any files on internet are more recent than here") print("check if any files on internet are more recent than here")
@ -104,9 +106,11 @@ struct MainView: View {
private func _startImporting() { private func _startImporting() {
importingFiles = true importingFiles = true
Task { Task {
lastDataSource = await FileImportManager.shared.importDataFromFFT() let lastDataSource = await FileImportManager.shared.importDataFromFFT()
dataStore.appSettings.lastDataSource = lastDataSource
dataStore.updateSettings()
if let lastDataSource, let mostRecentDate = URL.importDateFormatter.date(from: lastDataSource) { if let lastDataSource, let mostRecentDate = URL.importDateFormatter.date(from: lastDataSource) {
await _calculateCurrentUnrankedValues(mostRecentDateAvailable: mostRecentDate) await MonthData.calculateCurrentUnrankedValues(mostRecentDateAvailable: mostRecentDate)
} }
importingFiles = false importingFiles = false
@ -114,11 +118,6 @@ struct MainView: View {
} }
} }
private func _calculateCurrentUnrankedValues(mostRecentDateAvailable: Date) async {
lastDataSourceMaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: mostRecentDateAvailable, man: true)
lastDataSourceFemaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: mostRecentDateAvailable, man: false)
}
private func _downloadPreviousDate() async { private func _downloadPreviousDate() async {
await SourceFileManager.shared.getAllFiles() await SourceFileManager.shared.getAllFiles()
} }

@ -24,6 +24,7 @@ struct TournamentOrganizerView: View {
ContentUnavailableView("Aucun tournoi sélectionné", systemImage: "rectangle.slash", description: Text("Utilisez l'accès rapide ci-dessous pour éditer un tournoi et passer rapidement d'un tournoi à l'autre.")) ContentUnavailableView("Aucun tournoi sélectionné", systemImage: "rectangle.slash", description: Text("Utilisez l'accès rapide ci-dessous pour éditer un tournoi et passer rapidement d'un tournoi à l'autre."))
.navigationTitle("Gestionnaire de tournois") .navigationTitle("Gestionnaire de tournois")
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
} }
} }
Divider() Divider()

@ -13,7 +13,12 @@ struct PadelClubView: View {
@State private var checkingFiles: Bool = false @State private var checkingFiles: Bool = false
@State private var importingFiles: Bool = false @State private var importingFiles: Bool = false
@AppStorage("lastDataSource") var lastDataSource: String? @EnvironmentObject var dataStore: DataStore
var lastDataSource: String? {
dataStore.appSettings.lastDataSource
}
@Environment(\.managedObjectContext) private var viewContext @Environment(\.managedObjectContext) private var viewContext
@FetchRequest( @FetchRequest(
@ -36,17 +41,38 @@ struct PadelClubView: View {
List { List {
if let _lastDataSourceDate { if let _lastDataSourceDate {
Section { Section {
HStack { LabeledContent {
VStack(alignment: .leading) {
Text("Classement mensuel utilisé").font(.caption).foregroundStyle(.secondary)
Text(_lastDataSourceDate.monthYearFormatted)
}
Spacer()
Image(systemName: "checkmark") Image(systemName: "checkmark")
} label: {
Text(_lastDataSourceDate.monthYearFormatted)
Text("Classement mensuel utilisé")
} }
} }
} }
let monthData = dataStore.monthData.sorted(by: \.creationDate).reversed()
ForEach(monthData) { monthData in
Section {
LabeledContent {
if let maleUnrankedValue = monthData.maleUnrankedValue {
Text(maleUnrankedValue.formatted())
}
} label: {
Text("Messieurs")
Text("Rang d'un non classé")
}
LabeledContent {
if let femaleUnrankedValue = monthData.femaleUnrankedValue {
Text(femaleUnrankedValue.formatted())
}
} label: {
Text("Dames")
Text("Rang d'une non classée")
}
} header: {
Text(monthData.monthKey)
}
}
// //
// if players.isEmpty { // if players.isEmpty {
// ContentUnavailableView { // ContentUnavailableView {
@ -60,6 +86,7 @@ struct PadelClubView: View {
// } // }
// } // }
} }
.headerProminence(.increased)
.navigationTitle(TabDestination.padelClub.title) .navigationTitle(TabDestination.padelClub.title)
// .task { // .task {
// await self._checkSourceFileAvailability() // await self._checkSourceFileAvailability()
@ -101,7 +128,12 @@ struct PadelClubView: View {
private func _startImporting() { private func _startImporting() {
importingFiles = true importingFiles = true
Task { Task {
lastDataSource = await FileImportManager.shared.importDataFromFFT() let lastDataSource = await FileImportManager.shared.importDataFromFFT()
dataStore.appSettings.lastDataSource = lastDataSource
dataStore.updateSettings()
if let lastDataSource, let mostRecentDate = URL.importDateFormatter.date(from: lastDataSource) {
await MonthData.calculateCurrentUnrankedValues(mostRecentDateAvailable: mostRecentDate)
}
importingFiles = false importingFiles = false
} }
} }

@ -0,0 +1,25 @@
//
// DurationSettingsView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import SwiftUI
struct DurationSettingsView: View {
var body: some View {
List {
ForEach(MatchFormat.allCases, id: \.self) { matchFormat in
MatchFormatStorageView(matchFormat: matchFormat)
}
}
.navigationTitle("Durées moyennes")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
}
}
#Preview {
DurationSettingsView()
}

@ -0,0 +1,67 @@
//
// GlobalSettingsView.swift
// Padel Tournament
//
// Created by Razmig Sarkissian on 16/10/2023.
//
import SwiftUI
struct GlobalSettingsView: View {
@EnvironmentObject var dataStore : DataStore
var body: some View {
@Bindable var appSettings = dataStore.appSettings
List {
Section {
Picker(selection: $appSettings.groupStageMatchFormatPreference) {
Text("Automatique").tag(nil as Int?)
ForEach(MatchFormat.allCases, id: \.self) { format in
Text(format.format).tag(format.rawValue as Int?)
}
} label: {
HStack {
Text("Poule")
Spacer()
}
}
Picker(selection: $appSettings.bracketMatchFormatPreference) {
Text("Automatique").tag(nil as Int?)
ForEach(MatchFormat.allCases, id: \.self) { format in
Text(format.format).tag(format.rawValue as Int?)
}
} label: {
HStack {
Text("Tableau")
Spacer()
}
}
Picker(selection: $appSettings.loserBracketMatchFormatPreference) {
Text("Automatique").tag(nil as Int?)
ForEach(MatchFormat.allCases, id: \.self) { format in
Text(format.format).tag(format.rawValue as Int?)
}
} label: {
HStack {
Text("Match de classement")
Spacer()
}
}
} header: {
Text("Vos formats préférés")
} footer: {
Text("À minima, les règles fédérales seront toujours prises en compte par défaut.")
}
}
.onChange(of: [
appSettings.bracketMatchFormatPreference,
appSettings.groupStageMatchFormatPreference,
appSettings.loserBracketMatchFormatPreference
]) {
dataStore.updateSettings()
}
.navigationTitle("Formats par défaut")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
}
}

@ -0,0 +1,50 @@
//
// MatchFormatStorageView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import SwiftUI
struct MatchFormatStorageView: View {
@State private var estimatedDuration: Int
@EnvironmentObject var dataStore: DataStore
let matchFormat: MatchFormat
init(matchFormat: MatchFormat) {
self.matchFormat = matchFormat
_estimatedDuration = State(wrappedValue: matchFormat.getEstimatedDuration())
}
var body: some View {
Section {
LabeledContent {
StepperView(title: "minutes", count: $estimatedDuration, step: 5)
} label: {
Text("Durée \(matchFormat.format)")
Text(matchFormat.computedShortLabelWithoutPrefix)
}
} footer: {
if estimatedDuration != matchFormat.defaultEstimatedDuration {
HStack {
Spacer()
Button {
self.estimatedDuration = matchFormat.defaultEstimatedDuration
} label: {
Text("remettre la durée par défault")
.underline()
}
.buttonStyle(.borderless)
}
}
}
.onChange(of: estimatedDuration) {
dataStore.appSettings.saveMatchFormatsDefaultDuration(matchFormat, estimatedDuration: estimatedDuration)
dataStore.updateSettings()
}
}
}

@ -11,17 +11,34 @@ struct ToolboxView: View {
var body: some View { var body: some View {
NavigationStack { NavigationStack {
List { List {
Section {
NavigationLink { NavigationLink {
SelectablePlayerListView() SelectablePlayerListView()
} label: { } label: {
Label("Rechercher un joueur", systemImage: "person.fill.viewfinder") Label("Rechercher un joueur", systemImage: "person.fill.viewfinder")
} }
}
Section {
NavigationLink { NavigationLink {
RankCalculatorView() RankCalculatorView()
} label: { } label: {
Label("Calculateur de points", systemImage: "scalemass") Label("Calculateur de points", systemImage: "scalemass")
} }
}
Section {
NavigationLink {
GlobalSettingsView()
} label: {
Label("Formats de jeu par défaut", systemImage: "megaphone")
}
NavigationLink {
DurationSettingsView()
} label: {
Label("Estimation des durées moyennes", systemImage: "deskclock")
}
}
} }
.navigationTitle(TabDestination.toolbox.title) .navigationTitle(TabDestination.toolbox.title)
} }

@ -61,7 +61,6 @@ struct UmpireView: View {
user.licenceId = nil user.licenceId = nil
dataStore.setUser(user) dataStore.setUser(user)
} }
.font(.caption)
} }
} }
@ -80,7 +79,6 @@ struct UmpireView: View {
user.club = nil user.club = nil
dataStore.setUser(user) dataStore.setUser(user)
} }
.font(.caption)
} }
} }
} }

@ -194,11 +194,12 @@ struct PlanningSettingsView: View {
dispatch.timedMatches.forEach { matchSchedule in dispatch.timedMatches.forEach { matchSchedule in
if let match = matches.first(where: { $0.id == matchSchedule.matchID }) { if let match = matches.first(where: { $0.id == matchSchedule.matchID }) {
let timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(match.matchFormat.estimatedDuration) * 60 let estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)
let timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60
if let startDate = match.groupStageObject?.startDate { if let startDate = match.groupStageObject?.startDate {
let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd) let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd)
match.startDate = matchStartDate match.startDate = matchStartDate
lastDate = matchStartDate.addingTimeInterval(Double(match.matchFormat.estimatedDuration) * 60) lastDate = matchStartDate.addingTimeInterval(Double(estimatedDuration) * 60)
} }
match.setCourt(matchSchedule.courtIndex + 1) match.setCourt(matchSchedule.courtIndex + 1)
} }

@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
struct MatchFormatPickerView: View { struct MatchFormatPickerView: View {
@Environment(Tournament.self) var tournament: Tournament
let headerLabel: String let headerLabel: String
@Binding var matchFormat: MatchFormat @Binding var matchFormat: MatchFormat
@State private var isExpanded: Bool = false @State private var isExpanded: Bool = false
@ -40,7 +41,7 @@ struct MatchFormatPickerView: View {
Text(matchFormat.format).font(.largeTitle) Text(matchFormat.format).font(.largeTitle)
Spacer() Spacer()
VStack(alignment: .trailing) { VStack(alignment: .trailing) {
Text("~" + matchFormat.formattedEstimatedDuration()) Text("~" + matchFormat.formattedEstimatedDuration(tournament.additionalEstimationDuration))
Text(matchFormat.formattedEstimatedBreakDuration() + " de pause").foregroundStyle(.secondary).font(.subheadline) Text(matchFormat.formattedEstimatedBreakDuration() + " de pause").foregroundStyle(.secondary).font(.subheadline)
} }
} }

@ -18,9 +18,14 @@ struct SelectablePlayerListView: View {
let playerSelectionAction: PlayerSelectionAction? let playerSelectionAction: PlayerSelectionAction?
let contentUnavailableAction: ContentUnavailableAction? let contentUnavailableAction: ContentUnavailableAction?
@EnvironmentObject var dataStore: DataStore
@StateObject private var searchViewModel: SearchViewModel @StateObject private var searchViewModel: SearchViewModel
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@AppStorage("lastDataSource") var lastDataSource: String?
var lastDataSource: String? {
dataStore.appSettings.lastDataSource
}
@AppStorage("importingFiles") var importingFiles: Bool = false @AppStorage("importingFiles") var importingFiles: Bool = false
@State private var searchText: String = "" @State private var searchText: String = ""

@ -57,7 +57,7 @@ struct TournamentClubSettingsView: View {
TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount, max: 100) TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount, max: 100)
} }
} }
.onDisappear { .onChange(of: tournament.courtCount) {
try? dataStore.tournaments.addOrUpdate(instance: tournament) try? dataStore.tournaments.addOrUpdate(instance: tournament)
} }
} }

@ -58,7 +58,6 @@ struct TournamentGeneralSettingsView: View {
} }
.focused($textFieldIsFocus) .focused($textFieldIsFocus)
.scrollDismissesKeyboard(.immediately) .scrollDismissesKeyboard(.immediately)
.navigationTitle("Réglages")
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)
.toolbar { .toolbar {
ToolbarItem(placement: .keyboard) { ToolbarItem(placement: .keyboard) {
@ -67,10 +66,29 @@ struct TournamentGeneralSettingsView: View {
} }
} }
} }
.onDisappear { .onChange(of: tournament.startDate) {
try? dataStore.tournaments.addOrUpdate(instance: tournament) _save()
}
.onChange(of: tournament.entryFee) {
_save()
}
.onChange(of: tournament.name) {
_save()
}
.onChange(of: [
tournament.dayDuration,
tournament.federalCategory,
tournament.federalLevelCategory,
tournament.federalAgeCategory,
tournament.groupStageSortMode,
]) {
_save()
} }
} }
private func _save() {
try? dataStore.tournaments.addOrUpdate(instance: tournament)
}
} }
#Preview { #Preview {

@ -8,11 +8,97 @@
import SwiftUI import SwiftUI
struct TournamentMatchFormatsSettingsView: View { struct TournamentMatchFormatsSettingsView: View {
@Environment(NavigationViewModel.self) var navigation: NavigationViewModel
@Environment(Tournament.self) var tournament: Tournament
@EnvironmentObject var dataStore: DataStore
@State private var confirmUpdate: Bool = false
@State private var updateCompleted: Bool = false
var body: some View { var body: some View {
@Bindable var tournament = tournament
List { List {
if confirmUpdate {
RowButtonView("Modifier les matchs existants", role: .destructive) {
_updateAllFormat()
}
}
TournamentFormatSelectionView() TournamentFormatSelectionView()
Section {
LabeledContent {
StepperView(title: "minutes", count: $tournament.additionalEstimationDuration, step: 5)
} label: {
Text("Modifier les durées moyennes")
}
} footer: {
Text("Cette valeur est rajoutée ou soustraite aux valeurs par défaut. Par exemple, cela peut aider à mieux planifier un tournoi débutant ou jeune.")
}
Section {
NavigationLink {
DurationSettingsView()
} label: {
Label("Estimation des durées moyennes", systemImage: "deskclock")
}
}
}
.onChange(of: [tournament.roundFormat,
tournament.groupStageFormat,
tournament.loserRoundFormat,
]) {
_save()
_confirmOrSave()
}
.onChange(of: tournament.additionalEstimationDuration) {
_save()
}
.onChange(of: dataStore.appSettings.matchFormatsDefaultDuration) {
_confirmOrSave()
}
.overlay(alignment: .bottom) {
if updateCompleted {
Label("Formats mis à jour", systemImage: "checkmark.circle.fill")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
} }
} }
private func _confirmOrSave() {
switch tournament.state() {
case .initial:
break
case .build:
confirmUpdate = true
}
}
private func _updateAllFormat() {
updateCompleted = false
let groupStages = tournament.groupStages()
groupStages.forEach { groupStage in
groupStage.updateMatchFormat(tournament.groupStageMatchFormat)
}
let allRounds = tournament.allRounds()
allRounds.forEach { round in
if round.isLoserBracket() {
round.updateMatchFormat(tournament.loserBracketMatchFormat)
} else {
round.updateMatchFormat(tournament.matchFormat)
}
}
try? dataStore.groupStages.addOrUpdate(contentOfs: groupStages)
try? dataStore.rounds.addOrUpdate(contentOfs: allRounds)
confirmUpdate = false
updateCompleted = true
}
private func _save() {
try? dataStore.tournaments.addOrUpdate(instance: tournament)
}
} }
#Preview { #Preview {

@ -16,6 +16,7 @@ struct TournamentInitView: View {
NavigationLink(value: Screen.settings) { NavigationLink(value: Screen.settings) {
LabeledContent { LabeledContent {
Text(tournament.settingsDescriptionLocalizedLabel()) Text(tournament.settingsDescriptionLocalizedLabel())
.tint(.master)
} label: { } label: {
LabelSettings() LabelSettings()
} }
@ -28,6 +29,7 @@ struct TournamentInitView: View {
NavigationLink(value: Screen.structure) { NavigationLink(value: Screen.structure) {
LabeledContent { LabeledContent {
Text(tournament.structureDescriptionLocalizedLabel()) Text(tournament.structureDescriptionLocalizedLabel())
.tint(.master)
} label: { } label: {
LabelStructure() LabelStructure()
} }

@ -11,7 +11,10 @@ struct TournamentView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament @Environment(Tournament.self) var tournament: Tournament
var presentationContext: PresentationContext = .agenda var presentationContext: PresentationContext = .agenda
@AppStorage("lastDataSource") var lastDataSource: String?
var lastDataSource: String? {
dataStore.appSettings.lastDataSource
}
var _lastDataSourceDate: Date? { var _lastDataSourceDate: Date? {
guard let lastDataSource else { return nil } guard let lastDataSource else { return nil }
@ -91,6 +94,7 @@ struct TournamentView: View {
.environment(tournament) .environment(tournament)
}) })
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.toolbar { .toolbar {
ToolbarItem(placement: .principal) { ToolbarItem(placement: .principal) {
VStack { VStack {

Loading…
Cancel
Save