From 09b4055985a2da53620186657ce2f12266b2bd93 Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Mon, 13 Oct 2025 14:54:54 -0600 Subject: [PATCH] Moved to Ascently --- .github/workflows/deploy.yml | 6 +- README.md | 10 +- android/README.md | 6 +- android/app/build.gradle.kts | 4 +- android/app/build.gradle.kts.backup | 4 +- android/app/src/main/AndroidManifest.xml | 4 +- .../{openclimb => ascently}/MainActivity.kt | 16 +- .../data/database/Converters.kt | 4 +- .../data/database/OpenClimbDatabase.kt | 16 +- .../data/database/dao/AttemptDao.kt | 6 +- .../data/database/dao/ClimbSessionDao.kt | 6 +- .../data/database/dao/GymDao.kt | 6 +- .../data/database/dao/ProblemDao.kt | 6 +- .../data/format/BackupFormat.kt | 6 +- .../data/health/HealthConnectStub.kt | 12 +- .../data/model/Attempt.kt | 4 +- .../data/model/ClimbSession.kt | 4 +- .../data/model/ClimbType.kt | 2 +- .../data/model/DifficultySystem.kt | 2 +- .../{openclimb => ascently}/data/model/Gym.kt | 4 +- .../data/model/Problem.kt | 4 +- .../data/repository/ClimbRepository.kt | 28 +-- .../data/state/DataStateManager.kt | 7 +- .../data/sync/SyncService.kt | 22 +-- .../navigation/BottomNavigationItem.kt | 2 +- .../navigation/Screen.kt | 2 +- .../service/SessionTrackingService.kt | 20 +- .../ui/AscentlyApp.kt} | 46 ++--- .../ui/components/ActiveSessionBanner.kt | 8 +- .../ui/components/BarChart.kt | 2 +- .../ui/components/FullscreenImageViewer.kt | 2 +- .../ui/components/ImageDisplay.kt | 2 +- .../ui/components/ImagePicker.kt | 4 +- .../ui/components/LineChart.kt | 2 +- .../NotificationPermissionDialog.kt | 76 ++++++++ .../ui/components/OrientationAwareImage.kt | 4 +- .../ui/components/SyncIndicator.kt | 2 +- .../ui/health/HealthConnectCard.kt | 4 +- .../ui/screens/AddEditScreens.kt | 8 +- .../ui/screens/AnalyticsScreen.kt | 30 +-- .../ui/screens/DetailScreens.kt | 12 +- .../ui/screens/GymsScreen.kt | 12 +- .../ui/screens/ProblemsScreen.kt | 20 +- .../ui/screens/SessionsScreen.kt | 16 +- .../ui/screens/SettingsScreen.kt | 22 +-- .../{openclimb => ascently}/ui/theme/Color.kt | 2 +- .../ui/theme/CustomIcons.kt | 2 +- .../{openclimb => ascently}/ui/theme/Theme.kt | 4 +- .../{openclimb => ascently}/ui/theme/Type.kt | 2 +- .../ui/viewmodel/ClimbViewModel.kt | 26 +-- .../ui/viewmodel/ClimbViewModelFactory.kt | 6 +- .../utils/DateFormatUtils.kt | 2 +- .../utils/ImageNamingUtils.kt | 2 +- .../utils/ImageUtils.kt | 2 +- .../ascently/utils/MigrationManager.kt | 175 ++++++++++++++++++ .../utils/NotificationPermissionUtils.kt | 2 +- .../utils/SessionShareUtils.kt | 8 +- .../utils/ShortcutManager.kt | 14 +- .../utils/ZipExportImportUtils.kt | 14 +- .../widget/ClimbStatsWidgetProvider.kt | 16 +- .../NotificationPermissionDialog.kt | 89 --------- .../main/res/layout/widget_climb_stats.xml | 2 +- android/app/src/main/res/values/strings.xml | 2 +- android/app/src/main/res/values/themes.xml | 6 +- .../atridad/openclimb/BusinessLogicTests.kt | 6 +- .../com/atridad/openclimb/DataModelTests.kt | 6 +- .../atridad/openclimb/SyncMergeLogicTest.kt | 6 +- .../com/atridad/openclimb/UtilityTests.kt | 2 +- android/settings.gradle.kts | 4 +- .../LiveActivityManager.swift | 0 .../project.pbxproj | 114 ++++++------ .../contents.xcworkspacedata | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../IDEFindNavigatorScopes.plist | 0 .../UserInterfaceState.xcuserstate | Bin 0 -> 227444 bytes .../WorkspaceSettings.xcsettings | 0 .../xcshareddata/xcschemes/OpenClimb.xcscheme | 24 +-- .../SessionStatusLiveExtension.xcscheme | 26 +-- .../xcschemes/xcschememanagement.plist | 7 +- .../Ascently.entitlements} | 2 +- .../AscentlyApp.swift} | 3 +- .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../AppIcon.appiconset/app_icon_1024.png | Bin .../AppIcon.appiconset/app_icon_1024_dark.png | Bin .../app_icon_1024_tinted.png | Bin .../app_icon_dark_template.svg | 0 .../app_icon_light_template.svg | 0 .../app_icon_tinted_template.svg | 0 .../AppLogo.imageset/Contents.json | 0 .../AppLogo.imageset/app_logo_256.png | Bin .../AppLogo.imageset/app_logo_256_dark.png | Bin .../Assets.xcassets/Contents.json | 0 .../Components/AsyncImageView.swift | 0 .../Components/CameraImagePicker.swift | 0 .../Components/PhotoOptionSheet.swift | 0 ios/{OpenClimb => Ascently}/ContentView.swift | 0 ios/{OpenClimb => Ascently}/Info.plist | 0 .../Models/ActivityAttributes.swift | 0 .../Models/BackupFormat.swift | 2 +- .../Models/DataModels.swift | 0 .../Services/HealthKitService.swift | 0 .../Services/SyncService.swift | 4 +- .../Utils/AppIconHelper.swift | 0 .../Utils/DataStateManager.swift | 4 +- .../Utils/IconTestView.swift | 0 .../Utils/ImageManager.swift | 59 +++++- .../Utils/ImageNamingUtils.swift | 0 .../Utils/OrientationAwareImage.swift | 0 .../Utils/ZipUtils.swift | 4 +- .../ViewModels/ClimbingDataManager.swift | 84 ++++++++- .../ViewModels/LiveActivityManager.swift | 0 .../Views/AddEdit/AddAttemptView.swift | 0 .../Views/AddEdit/AddEditGymView.swift | 0 .../Views/AddEdit/AddEditProblemView.swift | 0 .../Views/AddEdit/AddEditSessionView.swift | 0 .../Views/AnalyticsView.swift | 0 .../Views/Detail/GymDetailView.swift | 0 .../Views/Detail/ProblemDetailView.swift | 0 .../Views/Detail/SessionDetailView.swift | 0 .../Views/GymsView.swift | 0 .../Views/LiveActivityDebugView.swift | 0 .../Views/ProblemsView.swift | 0 .../Views/SessionsView.swift | 0 .../Views/SettingsView.swift | 6 +- .../AscentlyTests.swift} | 2 +- .../UserInterfaceState.xcuserstate | Bin 166689 -> 0 bytes ios/README.md | 9 +- ios/SessionStatusLive/SessionStatusLive.swift | 8 +- .../SessionStatusLiveControl.swift | 2 +- ios/SessionStatusLiveExtension.entitlements | 2 +- sync/.env.example | 2 +- sync/.gitignore | 2 +- sync/README.md | 14 +- sync/docker-compose.yml | 2 +- sync/go.mod | 2 +- sync/main.go | 2 +- 137 files changed, 788 insertions(+), 483 deletions(-) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/MainActivity.kt (78%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/database/Converters.kt (95%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/database/OpenClimbDatabase.kt (90%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/database/dao/AttemptDao.kt (95%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/database/dao/ClimbSessionDao.kt (94%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/database/dao/GymDao.kt (89%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/database/dao/ProblemDao.kt (93%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/format/BackupFormat.kt (98%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/health/HealthConnectStub.kt (98%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/model/Attempt.kt (96%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/model/ClimbSession.kt (96%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/model/ClimbType.kt (86%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/model/DifficultySystem.kt (99%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/model/Gym.kt (93%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/model/Problem.kt (96%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/repository/ClimbRepository.kt (94%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/state/DataStateManager.kt (92%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/data/sync/SyncService.kt (97%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/navigation/BottomNavigationItem.kt (96%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/navigation/Screen.kt (95%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/service/SessionTrackingService.kt (94%) rename android/app/src/main/java/com/atridad/{openclimb/ui/OpenClimbApp.kt => ascently/ui/AscentlyApp.kt} (92%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/components/ActiveSessionBanner.kt (95%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/components/BarChart.kt (99%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/components/FullscreenImageViewer.kt (99%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/components/ImageDisplay.kt (98%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/components/ImagePicker.kt (99%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/components/LineChart.kt (99%) create mode 100644 android/app/src/main/java/com/atridad/ascently/ui/components/NotificationPermissionDialog.kt rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/components/OrientationAwareImage.kt (98%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/components/SyncIndicator.kt (97%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/health/HealthConnectCard.kt (99%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/screens/AddEditScreens.kt (99%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/screens/AnalyticsScreen.kt (95%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/screens/DetailScreens.kt (99%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/screens/GymsScreen.kt (93%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/screens/ProblemsScreen.kt (96%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/screens/SessionsScreen.kt (95%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/screens/SettingsScreen.kt (98%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/theme/Color.kt (98%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/theme/CustomIcons.kt (94%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/theme/Theme.kt (98%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/theme/Type.kt (92%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/viewmodel/ClimbViewModel.kt (96%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/ui/viewmodel/ClimbViewModelFactory.kt (80%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/utils/DateFormatUtils.kt (97%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/utils/ImageNamingUtils.kt (99%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/utils/ImageUtils.kt (99%) create mode 100644 android/app/src/main/java/com/atridad/ascently/utils/MigrationManager.kt rename android/app/src/main/java/com/atridad/{openclimb => ascently}/utils/NotificationPermissionUtils.kt (95%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/utils/SessionShareUtils.kt (99%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/utils/ShortcutManager.kt (90%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/utils/ZipExportImportUtils.kt (97%) rename android/app/src/main/java/com/atridad/{openclimb => ascently}/widget/ClimbStatsWidgetProvider.kt (94%) delete mode 100644 android/app/src/main/java/com/atridad/openclimb/ui/components/NotificationPermissionDialog.kt rename ios/{OpenClimb.xcodeproj => Ascently.xcodeproj}/LiveActivityManager.swift (100%) rename ios/{OpenClimb.xcodeproj => Ascently.xcodeproj}/project.pbxproj (87%) rename ios/{OpenClimb.xcodeproj => Ascently.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (100%) rename ios/{OpenClimb.xcodeproj => Ascently.xcodeproj}/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename ios/{OpenClimb.xcodeproj => Ascently.xcodeproj}/project.xcworkspace/xcuserdata/atridad.xcuserdatad/IDEFindNavigatorScopes.plist (100%) create mode 100644 ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate rename ios/{OpenClimb.xcodeproj => Ascently.xcodeproj}/project.xcworkspace/xcuserdata/atridad.xcuserdatad/WorkspaceSettings.xcsettings (100%) rename ios/{OpenClimb.xcodeproj => Ascently.xcodeproj}/xcshareddata/xcschemes/OpenClimb.xcscheme (80%) rename ios/{OpenClimb.xcodeproj => Ascently.xcodeproj}/xcshareddata/xcschemes/SessionStatusLiveExtension.xcscheme (85%) rename ios/{OpenClimb.xcodeproj => Ascently.xcodeproj}/xcuserdata/atridad.xcuserdatad/xcschemes/xcschememanagement.plist (80%) rename ios/{OpenClimb/OpenClimb.entitlements => Ascently/Ascently.entitlements} (88%) rename ios/{OpenClimb/OpenClimbApp.swift => Ascently/AscentlyApp.swift} (80%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppIcon.appiconset/app_icon_1024_dark.png (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppIcon.appiconset/app_icon_1024_tinted.png (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppIcon.appiconset/app_icon_dark_template.svg (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppIcon.appiconset/app_icon_light_template.svg (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppIcon.appiconset/app_icon_tinted_template.svg (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppLogo.imageset/Contents.json (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppLogo.imageset/app_logo_256.png (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/AppLogo.imageset/app_logo_256_dark.png (100%) rename ios/{OpenClimb => Ascently}/Assets.xcassets/Contents.json (100%) rename ios/{OpenClimb => Ascently}/Components/AsyncImageView.swift (100%) rename ios/{OpenClimb => Ascently}/Components/CameraImagePicker.swift (100%) rename ios/{OpenClimb => Ascently}/Components/PhotoOptionSheet.swift (100%) rename ios/{OpenClimb => Ascently}/ContentView.swift (100%) rename ios/{OpenClimb => Ascently}/Info.plist (100%) rename ios/{OpenClimb => Ascently}/Models/ActivityAttributes.swift (100%) rename ios/{OpenClimb => Ascently}/Models/BackupFormat.swift (99%) rename ios/{OpenClimb => Ascently}/Models/DataModels.swift (100%) rename ios/{OpenClimb => Ascently}/Services/HealthKitService.swift (100%) rename ios/{OpenClimb => Ascently}/Services/SyncService.swift (99%) rename ios/{OpenClimb => Ascently}/Utils/AppIconHelper.swift (100%) rename ios/{OpenClimb => Ascently}/Utils/DataStateManager.swift (95%) rename ios/{OpenClimb => Ascently}/Utils/IconTestView.swift (100%) rename ios/{OpenClimb => Ascently}/Utils/ImageManager.swift (93%) rename ios/{OpenClimb => Ascently}/Utils/ImageNamingUtils.swift (100%) rename ios/{OpenClimb => Ascently}/Utils/OrientationAwareImage.swift (100%) rename ios/{OpenClimb => Ascently}/Utils/ZipUtils.swift (99%) rename ios/{OpenClimb => Ascently}/ViewModels/ClimbingDataManager.swift (93%) rename ios/{OpenClimb => Ascently}/ViewModels/LiveActivityManager.swift (100%) rename ios/{OpenClimb => Ascently}/Views/AddEdit/AddAttemptView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/AddEdit/AddEditGymView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/AddEdit/AddEditProblemView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/AddEdit/AddEditSessionView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/AnalyticsView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/Detail/GymDetailView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/Detail/ProblemDetailView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/Detail/SessionDetailView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/GymsView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/LiveActivityDebugView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/ProblemsView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/SessionsView.swift (100%) rename ios/{OpenClimb => Ascently}/Views/SettingsView.swift (99%) rename ios/{OpenClimbTests/OpenClimbTests.swift => AscentlyTests/AscentlyTests.swift} (99%) delete mode 100644 ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2fe5c30..1389566 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: OpenClimb Docker Deploy +name: Ascently Docker Deploy on: push: branches: [main] @@ -34,5 +34,5 @@ jobs: platforms: linux/amd64 push: true tags: | - ${{ secrets.REPO_HOST }}/${{ github.repository_owner }}/openclimb-sync:${{ github.sha }} - ${{ secrets.REPO_HOST }}/${{ github.repository_owner }}/openclimb-sync:latest + ${{ secrets.REPO_HOST }}/${{ github.repository_owner }}/ascently-sync:${{ github.sha }} + ${{ secrets.REPO_HOST }}/${{ github.repository_owner }}/ascently-sync:latest diff --git a/README.md b/README.md index 575fc02..caece1a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# OpenClimb +# Ascently This is a FOSS app meant to help climbers track their sessions, routes/problems, and overall progress. This app is offline-only and requires no special permissions to run. Its built using Jetpack Compose with Material You support on Android and SwiftUI on iOS. @@ -7,7 +7,7 @@ This is a FOSS app meant to help climbers track their sessions, routes/problems, For Android do one of the following: 1. Download the latest APK from the Releases page -2. [Obtainium](https://apps.obtainium.imranr.dev/redirect?r=obtainium://app/%7B%22id%22%3A%22com.atridad.openclimb%22%2C%22url%22%3A%22https%3A%2F%2Fgit.atri.dad%2Fatridad%2FOpenClimb%2Freleases%22%2C%22author%22%3A%22git.atri.dad%22%2C%22name%22%3A%22OpenClimb%22%2C%22preferredApkIndex%22%3A0%2C%22additionalSettings%22%3A%22%7B%5C%22intermediateLink%5C%22%3A%5B%5D%2C%5C%22customLinkFilterRegex%5C%22%3A%5C%22%5C%22%2C%5C%22filterByLinkText%5C%22%3Afalse%2C%5C%22skipSort%5C%22%3Afalse%2C%5C%22reverseSort%5C%22%3Afalse%2C%5C%22sortByLastLinkSegment%5C%22%3Afalse%2C%5C%22versionExtractWholePage%5C%22%3Afalse%2C%5C%22requestHeader%5C%22%3A%5B%7B%5C%22requestHeader%5C%22%3A%5C%22User-Agent%3A%20Mozilla%2F5.0%20(Linux%3B%20Android%2010%3B%20K)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F114.0.0.0%20Mobile%20Safari%2F537.36%5C%22%7D%5D%2C%5C%22defaultPseudoVersioningMethod%5C%22%3A%5C%22partialAPKHash%5C%22%2C%5C%22trackOnly%5C%22%3Afalse%2C%5C%22versionExtractionRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22matchGroupToUse%5C%22%3A%5C%22%5C%22%2C%5C%22versionDetection%5C%22%3Afalse%2C%5C%22useVersionCodeAsOSVersion%5C%22%3Afalse%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22invertAPKFilter%5C%22%3Afalse%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22OpenClimb%5C%22%2C%5C%22appAuthor%5C%22%3A%5C%22%5C%22%2C%5C%22shizukuPretendToBeGooglePlay%5C%22%3Afalse%2C%5C%22allowInsecure%5C%22%3Afalse%2C%5C%22exemptFromBackgroundUpdates%5C%22%3Afalse%2C%5C%22skipUpdateNotifications%5C%22%3Afalse%2C%5C%22about%5C%22%3A%5C%22%5C%22%2C%5C%22refreshBeforeDownload%5C%22%3Afalse%7D%22%2C%22overrideSource%22%3Anull%7D) +2. [Obtainium](https://apps.obtainium.imranr.dev/redirect?r=obtainium://app/%7B%22id%22%3A%22com.atridad.ascently%22%2C%22url%22%3A%22https%3A%2F%2Fgit.atri.dad%2Fatridad%2FAscently%2Freleases%22%2C%22author%22%3A%22git.atri.dad%22%2C%22name%22%3A%22Ascently%22%2C%22preferredApkIndex%22%3A0%2C%22additionalSettings%22%3A%22%7B%5C%22intermediateLink%5C%22%3A%5B%5D%2C%5C%22customLinkFilterRegex%5C%22%3A%5C%22%5C%22%2C%5C%22filterByLinkText%5C%22%3Afalse%2C%5C%22skipSort%5C%22%3Afalse%2C%5C%22reverseSort%5C%22%3Afalse%2C%5C%22sortByLastLinkSegment%5C%22%3Afalse%2C%5C%22versionExtractWholePage%5C%22%3Afalse%2C%5C%22requestHeader%5C%22%3A%5B%7B%5C%22requestHeader%5C%22%3A%5C%22User-Agent%3A%20Mozilla%2F5.0%20(Linux%3B%20Android%2010%3B%20K)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F114.0.0.0%20Mobile%20Safari%2F537.36%5C%22%7D%5D%2C%5C%22defaultPseudoVersioningMethod%5C%22%3A%5C%22partialAPKHash%5C%22%2C%5C%22trackOnly%5C%22%3Afalse%2C%5C%22versionExtractionRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22matchGroupToUse%5C%22%3A%5C%22%5C%22%2C%5C%22versionDetection%5C%22%3Afalse%2C%5C%22useVersionCodeAsOSVersion%5C%22%3Afalse%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22invertAPKFilter%5C%22%3Afalse%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22Ascently%5C%22%2C%5C%22appAuthor%5C%22%3A%5C%22%5C%22%2C%5C%22shizukuPretendToBeGooglePlay%5C%22%3Afalse%2C%5C%22allowInsecure%5C%22%3Afalse%2C%5C%22exemptFromBackgroundUpdates%5C%22%3Afalse%2C%5C%22skipUpdateNotifications%5C%22%3Afalse%2C%5C%22about%5C%22%3A%5C%22%5C%22%2C%5C%22refreshBeforeDownload%5C%22%3Afalse%7D%22%2C%22overrideSource%22%3Anull%7D) For iOS: @@ -22,12 +22,12 @@ You can run your own sync server to keep your data in sync across devices. The s 1. Create a `.env` file with your configuration: ``` -IMAGE=git.atri.dad/atridad/openclimb-sync:latest +IMAGE=git.atri.dad/atridad/ascently-sync:latest APP_PORT=8080 AUTH_TOKEN=your-secure-auth-token-here -DATA_FILE=/data/openclimb.json +DATA_FILE=/data/ascently.json IMAGES_DIR=/data/images -ROOT_DIR=./openclimb-data +ROOT_DIR=./ascently-data ``` 2. Use the provided `docker-compose.yml` in the `sync/` directory: diff --git a/android/README.md b/android/README.md index 1944898..e18053f 100644 --- a/android/README.md +++ b/android/README.md @@ -1,10 +1,10 @@ -# OpenClimb for Android +# Ascently for Android -This is the native Android app for OpenClimb, built with Kotlin and Jetpack Compose. +This is the native Android app for Ascently, built with Kotlin and Jetpack Compose. ## Project Structure -This is a standard Android Gradle project. The main code lives in `app/src/main/java/com/atridad/openclimb/`. +This is a standard Android Gradle project. The main code lives in `app/src/main/java/com/atridad/ascently/`. - `data/`: Handles all the app's data. - `database/`: Room database setup (DAOs, entities). diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index acecb41..34bf92b 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -9,11 +9,11 @@ plugins { } android { - namespace = "com.atridad.openclimb" + namespace = "com.atridad.ascently" compileSdk = 36 defaultConfig { - applicationId = "com.atridad.openclimb" + applicationId = "com.atridad.ascently" minSdk = 31 targetSdk = 36 versionCode = 39 diff --git a/android/app/build.gradle.kts.backup b/android/app/build.gradle.kts.backup index 3d33435..8721e3c 100644 --- a/android/app/build.gradle.kts.backup +++ b/android/app/build.gradle.kts.backup @@ -9,11 +9,11 @@ plugins { } android { - namespace = "com.atridad.openclimb" + namespace = "com.atridad.ascently" compileSdk = 36 defaultConfig { - applicationId = "com.atridad.openclimb" + applicationId = "com.atridad.ascently" minSdk = 31 targetSdk = 36 versionCode = 27 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 52b0e91..1d0ffa4 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -50,13 +50,13 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.OpenClimb" + android:theme="@style/Theme.Ascently" tools:targetApi="31"> + android:theme="@style/Theme.Ascently.Splash"> diff --git a/android/app/src/main/java/com/atridad/openclimb/MainActivity.kt b/android/app/src/main/java/com/atridad/ascently/MainActivity.kt similarity index 78% rename from android/app/src/main/java/com/atridad/openclimb/MainActivity.kt rename to android/app/src/main/java/com/atridad/ascently/MainActivity.kt index 928a51f..7f66490 100644 --- a/android/app/src/main/java/com/atridad/openclimb/MainActivity.kt +++ b/android/app/src/main/java/com/atridad/ascently/MainActivity.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb +package com.atridad.ascently import android.content.Intent import android.os.Bundle @@ -9,8 +9,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Surface import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import com.atridad.openclimb.ui.OpenClimbApp -import com.atridad.openclimb.ui.theme.OpenClimbTheme +import com.atridad.ascently.ui.AscentlyApp +import com.atridad.ascently.ui.theme.AscentlyTheme +import com.atridad.ascently.utils.MigrationManager class MainActivity : ComponentActivity() { private var shortcutAction by mutableStateOf(null) @@ -23,16 +24,19 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setTheme(R.style.Theme_OpenClimb) + setTheme(R.style.Theme_Ascently) enableEdgeToEdge() + // Perform migration from OpenClimb to Ascently if needed + MigrationManager(this).migrateIfNeeded() + shortcutAction = intent?.action lastUsedGymId = intent?.getStringExtra("LAST_USED_GYM_ID") setContent { - OpenClimbTheme { + AscentlyTheme { Surface(modifier = Modifier.fillMaxSize()) { - OpenClimbApp( + AscentlyApp( shortcutAction = shortcutAction, lastUsedGymId = lastUsedGymId, onShortcutActionProcessed = { clearShortcutAction() } diff --git a/android/app/src/main/java/com/atridad/openclimb/data/database/Converters.kt b/android/app/src/main/java/com/atridad/ascently/data/database/Converters.kt similarity index 95% rename from android/app/src/main/java/com/atridad/openclimb/data/database/Converters.kt rename to android/app/src/main/java/com/atridad/ascently/data/database/Converters.kt index 8ab3cba..8bcd29e 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/database/Converters.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/database/Converters.kt @@ -1,7 +1,7 @@ -package com.atridad.openclimb.data.database +package com.atridad.ascently.data.database import androidx.room.TypeConverter -import com.atridad.openclimb.data.model.* +import com.atridad.ascently.data.model.* import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json diff --git a/android/app/src/main/java/com/atridad/openclimb/data/database/OpenClimbDatabase.kt b/android/app/src/main/java/com/atridad/ascently/data/database/OpenClimbDatabase.kt similarity index 90% rename from android/app/src/main/java/com/atridad/openclimb/data/database/OpenClimbDatabase.kt rename to android/app/src/main/java/com/atridad/ascently/data/database/OpenClimbDatabase.kt index 852988b..6f5fa36 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/database/OpenClimbDatabase.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/database/OpenClimbDatabase.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.data.database +package com.atridad.ascently.data.database import android.content.Context import androidx.room.Database @@ -7,8 +7,8 @@ import androidx.room.RoomDatabase import androidx.room.TypeConverters import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase -import com.atridad.openclimb.data.database.dao.* -import com.atridad.openclimb.data.model.* +import com.atridad.ascently.data.database.dao.* +import com.atridad.ascently.data.model.* @Database( entities = [Gym::class, Problem::class, ClimbSession::class, Attempt::class], @@ -16,7 +16,7 @@ import com.atridad.openclimb.data.model.* exportSchema = false ) @TypeConverters(Converters::class) -abstract class OpenClimbDatabase : RoomDatabase() { +abstract class AscentlyDatabase : RoomDatabase() { abstract fun gymDao(): GymDao abstract fun problemDao(): ProblemDao @@ -24,7 +24,7 @@ abstract class OpenClimbDatabase : RoomDatabase() { abstract fun attemptDao(): AttemptDao companion object { - @Volatile private var INSTANCE: OpenClimbDatabase? = null + @Volatile private var INSTANCE: AscentlyDatabase? = null val MIGRATION_4_5 = object : Migration(4, 5) { @@ -84,14 +84,14 @@ abstract class OpenClimbDatabase : RoomDatabase() { } } - fun getDatabase(context: Context): OpenClimbDatabase { + fun getDatabase(context: Context): AscentlyDatabase { return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, - OpenClimbDatabase::class.java, - "openclimb_database" + AscentlyDatabase::class.java, + "ascently_database" ) .addMigrations(MIGRATION_4_5, MIGRATION_5_6) .enableMultiInstanceInvalidation() diff --git a/android/app/src/main/java/com/atridad/openclimb/data/database/dao/AttemptDao.kt b/android/app/src/main/java/com/atridad/ascently/data/database/dao/AttemptDao.kt similarity index 95% rename from android/app/src/main/java/com/atridad/openclimb/data/database/dao/AttemptDao.kt rename to android/app/src/main/java/com/atridad/ascently/data/database/dao/AttemptDao.kt index 75adde1..4d86b68 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/database/dao/AttemptDao.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/database/dao/AttemptDao.kt @@ -1,8 +1,8 @@ -package com.atridad.openclimb.data.database.dao +package com.atridad.ascently.data.database.dao import androidx.room.* -import com.atridad.openclimb.data.model.Attempt -import com.atridad.openclimb.data.model.AttemptResult +import com.atridad.ascently.data.model.Attempt +import com.atridad.ascently.data.model.AttemptResult import kotlinx.coroutines.flow.Flow @Dao diff --git a/android/app/src/main/java/com/atridad/openclimb/data/database/dao/ClimbSessionDao.kt b/android/app/src/main/java/com/atridad/ascently/data/database/dao/ClimbSessionDao.kt similarity index 94% rename from android/app/src/main/java/com/atridad/openclimb/data/database/dao/ClimbSessionDao.kt rename to android/app/src/main/java/com/atridad/ascently/data/database/dao/ClimbSessionDao.kt index f42920d..452b3c9 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/database/dao/ClimbSessionDao.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/database/dao/ClimbSessionDao.kt @@ -1,8 +1,8 @@ -package com.atridad.openclimb.data.database.dao +package com.atridad.ascently.data.database.dao import androidx.room.* -import com.atridad.openclimb.data.model.ClimbSession -import com.atridad.openclimb.data.model.SessionStatus +import com.atridad.ascently.data.model.ClimbSession +import com.atridad.ascently.data.model.SessionStatus import kotlinx.coroutines.flow.Flow @Dao diff --git a/android/app/src/main/java/com/atridad/openclimb/data/database/dao/GymDao.kt b/android/app/src/main/java/com/atridad/ascently/data/database/dao/GymDao.kt similarity index 89% rename from android/app/src/main/java/com/atridad/openclimb/data/database/dao/GymDao.kt rename to android/app/src/main/java/com/atridad/ascently/data/database/dao/GymDao.kt index 3bc9a81..045ee54 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/database/dao/GymDao.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/database/dao/GymDao.kt @@ -1,8 +1,8 @@ -package com.atridad.openclimb.data.database.dao +package com.atridad.ascently.data.database.dao import androidx.room.* -import com.atridad.openclimb.data.model.ClimbType -import com.atridad.openclimb.data.model.Gym +import com.atridad.ascently.data.model.ClimbType +import com.atridad.ascently.data.model.Gym import kotlinx.coroutines.flow.Flow @Dao diff --git a/android/app/src/main/java/com/atridad/openclimb/data/database/dao/ProblemDao.kt b/android/app/src/main/java/com/atridad/ascently/data/database/dao/ProblemDao.kt similarity index 93% rename from android/app/src/main/java/com/atridad/openclimb/data/database/dao/ProblemDao.kt rename to android/app/src/main/java/com/atridad/ascently/data/database/dao/ProblemDao.kt index 6546258..5529e11 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/database/dao/ProblemDao.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/database/dao/ProblemDao.kt @@ -1,8 +1,8 @@ -package com.atridad.openclimb.data.database.dao +package com.atridad.ascently.data.database.dao import androidx.room.* -import com.atridad.openclimb.data.model.ClimbType -import com.atridad.openclimb.data.model.Problem +import com.atridad.ascently.data.model.ClimbType +import com.atridad.ascently.data.model.Problem import kotlinx.coroutines.flow.Flow @Dao diff --git a/android/app/src/main/java/com/atridad/openclimb/data/format/BackupFormat.kt b/android/app/src/main/java/com/atridad/ascently/data/format/BackupFormat.kt similarity index 98% rename from android/app/src/main/java/com/atridad/openclimb/data/format/BackupFormat.kt rename to android/app/src/main/java/com/atridad/ascently/data/format/BackupFormat.kt index fc3299c..2d96088 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/format/BackupFormat.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/format/BackupFormat.kt @@ -1,9 +1,9 @@ -package com.atridad.openclimb.data.format +package com.atridad.ascently.data.format -import com.atridad.openclimb.data.model.* +import com.atridad.ascently.data.model.* import kotlinx.serialization.Serializable -// Root structure for OpenClimb backup data +// Root structure for Ascently backup data @Serializable data class ClimbDataBackup( val exportedAt: String, diff --git a/android/app/src/main/java/com/atridad/openclimb/data/health/HealthConnectStub.kt b/android/app/src/main/java/com/atridad/ascently/data/health/HealthConnectStub.kt similarity index 98% rename from android/app/src/main/java/com/atridad/openclimb/data/health/HealthConnectStub.kt rename to android/app/src/main/java/com/atridad/ascently/data/health/HealthConnectStub.kt index a2500be..a8ce0e2 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/health/HealthConnectStub.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/health/HealthConnectStub.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.data.health +package com.atridad.ascently.data.health import android.annotation.SuppressLint import android.content.Context @@ -12,9 +12,9 @@ import androidx.health.connect.client.records.ExerciseSessionRecord import androidx.health.connect.client.records.HeartRateRecord import androidx.health.connect.client.records.TotalCaloriesBurnedRecord import androidx.health.connect.client.units.Energy -import com.atridad.openclimb.data.model.ClimbSession -import com.atridad.openclimb.data.model.SessionStatus -import com.atridad.openclimb.utils.DateFormatUtils +import com.atridad.ascently.data.model.ClimbSession +import com.atridad.ascently.data.model.SessionStatus +import com.atridad.ascently.utils.DateFormatUtils import java.time.Duration import java.time.Instant import java.time.ZoneOffset @@ -24,7 +24,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flow /** - * Health Connect manager for OpenClimb that syncs climbing sessions to Samsung Health, Google Fit, + * Health Connect manager for Ascently that syncs climbing sessions to Samsung Health, Google Fit, * and other health apps. */ @SuppressLint("RestrictedApi") @@ -192,7 +192,7 @@ class HealthConnectManager(private val context: Context) { } else { results.add("❌ Health Connect not ready") if (!available) results.add("- Health Connect not available on device") - if (!_isEnabled.value) results.add("- Not enabled in OpenClimb settings") + if (!_isEnabled.value) results.add("- Not enabled in Ascently settings") if (!hasPerms) results.add("- Permissions not granted") if (!_isCompatible.value) results.add("- API compatibility issues") } diff --git a/android/app/src/main/java/com/atridad/openclimb/data/model/Attempt.kt b/android/app/src/main/java/com/atridad/ascently/data/model/Attempt.kt similarity index 96% rename from android/app/src/main/java/com/atridad/openclimb/data/model/Attempt.kt rename to android/app/src/main/java/com/atridad/ascently/data/model/Attempt.kt index 92d4e57..4951e7d 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/model/Attempt.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/model/Attempt.kt @@ -1,10 +1,10 @@ -package com.atridad.openclimb.data.model +package com.atridad.ascently.data.model import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Index import androidx.room.PrimaryKey -import com.atridad.openclimb.utils.DateFormatUtils +import com.atridad.ascently.utils.DateFormatUtils import kotlinx.serialization.Serializable @Serializable diff --git a/android/app/src/main/java/com/atridad/openclimb/data/model/ClimbSession.kt b/android/app/src/main/java/com/atridad/ascently/data/model/ClimbSession.kt similarity index 96% rename from android/app/src/main/java/com/atridad/openclimb/data/model/ClimbSession.kt rename to android/app/src/main/java/com/atridad/ascently/data/model/ClimbSession.kt index 0c4b5c9..af1e026 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/model/ClimbSession.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/model/ClimbSession.kt @@ -1,10 +1,10 @@ -package com.atridad.openclimb.data.model +package com.atridad.ascently.data.model import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Index import androidx.room.PrimaryKey -import com.atridad.openclimb.utils.DateFormatUtils +import com.atridad.ascently.utils.DateFormatUtils import kotlinx.serialization.Serializable @Serializable diff --git a/android/app/src/main/java/com/atridad/openclimb/data/model/ClimbType.kt b/android/app/src/main/java/com/atridad/ascently/data/model/ClimbType.kt similarity index 86% rename from android/app/src/main/java/com/atridad/openclimb/data/model/ClimbType.kt rename to android/app/src/main/java/com/atridad/ascently/data/model/ClimbType.kt index 8647cee..841f89a 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/model/ClimbType.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/model/ClimbType.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.data.model +package com.atridad.ascently.data.model import kotlinx.serialization.Serializable diff --git a/android/app/src/main/java/com/atridad/openclimb/data/model/DifficultySystem.kt b/android/app/src/main/java/com/atridad/ascently/data/model/DifficultySystem.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/data/model/DifficultySystem.kt rename to android/app/src/main/java/com/atridad/ascently/data/model/DifficultySystem.kt index 39da548..8750109 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/model/DifficultySystem.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/model/DifficultySystem.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.data.model +package com.atridad.ascently.data.model import kotlinx.serialization.Serializable diff --git a/android/app/src/main/java/com/atridad/openclimb/data/model/Gym.kt b/android/app/src/main/java/com/atridad/ascently/data/model/Gym.kt similarity index 93% rename from android/app/src/main/java/com/atridad/openclimb/data/model/Gym.kt rename to android/app/src/main/java/com/atridad/ascently/data/model/Gym.kt index 1876cd9..c1670a4 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/model/Gym.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/model/Gym.kt @@ -1,8 +1,8 @@ -package com.atridad.openclimb.data.model +package com.atridad.ascently.data.model import androidx.room.Entity import androidx.room.PrimaryKey -import com.atridad.openclimb.utils.DateFormatUtils +import com.atridad.ascently.utils.DateFormatUtils import kotlinx.serialization.Serializable @Entity(tableName = "gyms") diff --git a/android/app/src/main/java/com/atridad/openclimb/data/model/Problem.kt b/android/app/src/main/java/com/atridad/ascently/data/model/Problem.kt similarity index 96% rename from android/app/src/main/java/com/atridad/openclimb/data/model/Problem.kt rename to android/app/src/main/java/com/atridad/ascently/data/model/Problem.kt index b983d22..2d3aa82 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/model/Problem.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/model/Problem.kt @@ -1,10 +1,10 @@ -package com.atridad.openclimb.data.model +package com.atridad.ascently.data.model import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Index import androidx.room.PrimaryKey -import com.atridad.openclimb.utils.DateFormatUtils +import com.atridad.ascently.utils.DateFormatUtils import kotlinx.serialization.Serializable @Entity( diff --git a/android/app/src/main/java/com/atridad/openclimb/data/repository/ClimbRepository.kt b/android/app/src/main/java/com/atridad/ascently/data/repository/ClimbRepository.kt similarity index 94% rename from android/app/src/main/java/com/atridad/openclimb/data/repository/ClimbRepository.kt rename to android/app/src/main/java/com/atridad/ascently/data/repository/ClimbRepository.kt index f028bae..6d8260c 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/repository/ClimbRepository.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/repository/ClimbRepository.kt @@ -1,25 +1,25 @@ -package com.atridad.openclimb.data.repository +package com.atridad.ascently.data.repository import android.content.Context import android.content.SharedPreferences import androidx.core.content.edit -import com.atridad.openclimb.data.database.OpenClimbDatabase -import com.atridad.openclimb.data.format.BackupAttempt -import com.atridad.openclimb.data.format.BackupClimbSession -import com.atridad.openclimb.data.format.BackupGym -import com.atridad.openclimb.data.format.BackupProblem -import com.atridad.openclimb.data.format.ClimbDataBackup -import com.atridad.openclimb.data.format.DeletedItem -import com.atridad.openclimb.data.model.* -import com.atridad.openclimb.data.state.DataStateManager -import com.atridad.openclimb.utils.DateFormatUtils -import com.atridad.openclimb.utils.ZipExportImportUtils +import com.atridad.ascently.data.database.AscentlyDatabase +import com.atridad.ascently.data.format.BackupAttempt +import com.atridad.ascently.data.format.BackupClimbSession +import com.atridad.ascently.data.format.BackupGym +import com.atridad.ascently.data.format.BackupProblem +import com.atridad.ascently.data.format.ClimbDataBackup +import com.atridad.ascently.data.format.DeletedItem +import com.atridad.ascently.data.model.* +import com.atridad.ascently.data.state.DataStateManager +import com.atridad.ascently.utils.DateFormatUtils +import com.atridad.ascently.utils.ZipExportImportUtils import java.io.File import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.serialization.json.Json -class ClimbRepository(database: OpenClimbDatabase, private val context: Context) { +class ClimbRepository(database: AscentlyDatabase, private val context: Context) { private val gymDao = database.gymDao() private val problemDao = database.problemDao() private val sessionDao = database.climbSessionDao() @@ -157,7 +157,7 @@ class ClimbRepository(database: OpenClimbDatabase, private val context: Context) .filter { imagePath -> try { val imageFile = - com.atridad.openclimb.utils.ImageUtils.getImageFile( + com.atridad.ascently.utils.ImageUtils.getImageFile( context, imagePath ) diff --git a/android/app/src/main/java/com/atridad/openclimb/data/state/DataStateManager.kt b/android/app/src/main/java/com/atridad/ascently/data/state/DataStateManager.kt similarity index 92% rename from android/app/src/main/java/com/atridad/openclimb/data/state/DataStateManager.kt rename to android/app/src/main/java/com/atridad/ascently/data/state/DataStateManager.kt index 85064c4..32ae8fb 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/state/DataStateManager.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/state/DataStateManager.kt @@ -1,10 +1,10 @@ -package com.atridad.openclimb.data.state +package com.atridad.ascently.data.state import android.content.Context import android.content.SharedPreferences import android.util.Log -import com.atridad.openclimb.utils.DateFormatUtils import androidx.core.content.edit +import com.atridad.ascently.utils.DateFormatUtils /** * Manages the overall data state timestamp for sync purposes. This tracks when any data in the @@ -14,7 +14,7 @@ class DataStateManager(context: Context) { companion object { private const val TAG = "DataStateManager" - private const val PREFS_NAME = "openclimb_data_state" + private const val PREFS_NAME = "ascently_data_state" private const val KEY_LAST_MODIFIED = "last_modified_timestamp" private const val KEY_INITIALIZED = "state_initialized" } @@ -58,5 +58,4 @@ class DataStateManager(context: Context) { private fun markAsInitialized() { prefs.edit { putBoolean(KEY_INITIALIZED, true) } } - } diff --git a/android/app/src/main/java/com/atridad/openclimb/data/sync/SyncService.kt b/android/app/src/main/java/com/atridad/ascently/data/sync/SyncService.kt similarity index 97% rename from android/app/src/main/java/com/atridad/openclimb/data/sync/SyncService.kt rename to android/app/src/main/java/com/atridad/ascently/data/sync/SyncService.kt index 9aac55b..cae5434 100644 --- a/android/app/src/main/java/com/atridad/openclimb/data/sync/SyncService.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/sync/SyncService.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.data.sync +package com.atridad.ascently.data.sync import android.content.Context import android.content.SharedPreferences @@ -7,16 +7,16 @@ import android.net.NetworkCapabilities import android.util.Log import androidx.annotation.RequiresPermission import androidx.core.content.edit -import com.atridad.openclimb.data.format.BackupAttempt -import com.atridad.openclimb.data.format.BackupClimbSession -import com.atridad.openclimb.data.format.BackupGym -import com.atridad.openclimb.data.format.BackupProblem -import com.atridad.openclimb.data.format.ClimbDataBackup -import com.atridad.openclimb.data.repository.ClimbRepository -import com.atridad.openclimb.data.state.DataStateManager -import com.atridad.openclimb.utils.DateFormatUtils -import com.atridad.openclimb.utils.ImageNamingUtils -import com.atridad.openclimb.utils.ImageUtils +import com.atridad.ascently.data.format.BackupAttempt +import com.atridad.ascently.data.format.BackupClimbSession +import com.atridad.ascently.data.format.BackupGym +import com.atridad.ascently.data.format.BackupProblem +import com.atridad.ascently.data.format.ClimbDataBackup +import com.atridad.ascently.data.repository.ClimbRepository +import com.atridad.ascently.data.state.DataStateManager +import com.atridad.ascently.utils.DateFormatUtils +import com.atridad.ascently.utils.ImageNamingUtils +import com.atridad.ascently.utils.ImageUtils import java.io.IOException import java.io.Serializable import java.util.concurrent.TimeUnit diff --git a/android/app/src/main/java/com/atridad/openclimb/navigation/BottomNavigationItem.kt b/android/app/src/main/java/com/atridad/ascently/navigation/BottomNavigationItem.kt similarity index 96% rename from android/app/src/main/java/com/atridad/openclimb/navigation/BottomNavigationItem.kt rename to android/app/src/main/java/com/atridad/ascently/navigation/BottomNavigationItem.kt index a966854..2a223c3 100644 --- a/android/app/src/main/java/com/atridad/openclimb/navigation/BottomNavigationItem.kt +++ b/android/app/src/main/java/com/atridad/ascently/navigation/BottomNavigationItem.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.navigation +package com.atridad.ascently.navigation import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* diff --git a/android/app/src/main/java/com/atridad/openclimb/navigation/Screen.kt b/android/app/src/main/java/com/atridad/ascently/navigation/Screen.kt similarity index 95% rename from android/app/src/main/java/com/atridad/openclimb/navigation/Screen.kt rename to android/app/src/main/java/com/atridad/ascently/navigation/Screen.kt index c459e07..f56fbfd 100644 --- a/android/app/src/main/java/com/atridad/openclimb/navigation/Screen.kt +++ b/android/app/src/main/java/com/atridad/ascently/navigation/Screen.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.navigation +package com.atridad.ascently.navigation import kotlinx.serialization.Serializable diff --git a/android/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt b/android/app/src/main/java/com/atridad/ascently/service/SessionTrackingService.kt similarity index 94% rename from android/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt rename to android/app/src/main/java/com/atridad/ascently/service/SessionTrackingService.kt index 091d45c..a4734b1 100644 --- a/android/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt +++ b/android/app/src/main/java/com/atridad/ascently/service/SessionTrackingService.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.service +package com.atridad.ascently.service import android.app.NotificationChannel import android.app.NotificationManager @@ -8,10 +8,10 @@ import android.content.Context import android.content.Intent import android.os.IBinder import androidx.core.app.NotificationCompat -import com.atridad.openclimb.MainActivity -import com.atridad.openclimb.R -import com.atridad.openclimb.data.database.OpenClimbDatabase -import com.atridad.openclimb.data.repository.ClimbRepository +import com.atridad.ascently.MainActivity +import com.atridad.ascently.R +import com.atridad.ascently.data.database.AscentlyDatabase +import com.atridad.ascently.data.repository.ClimbRepository import kotlinx.coroutines.* import kotlinx.coroutines.flow.firstOrNull import java.time.LocalDateTime @@ -52,7 +52,7 @@ class SessionTrackingService : Service() { override fun onCreate() { super.onCreate() - val database = OpenClimbDatabase.getDatabase(this) + val database = AscentlyDatabase.getDatabase(this) repository = ClimbRepository(database, this) notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager @@ -75,8 +75,8 @@ class SessionTrackingService : Service() { sessionId != null -> repository.getSessionById(sessionId) else -> repository.getActiveSession() } - if (targetSession != null && targetSession.status == com.atridad.openclimb.data.model.SessionStatus.ACTIVE) { - val completed = with(com.atridad.openclimb.data.model.ClimbSession) { targetSession.complete() } + if (targetSession != null && targetSession.status == com.atridad.ascently.data.model.SessionStatus.ACTIVE) { + val completed = with(com.atridad.ascently.data.model.ClimbSession) { targetSession.complete() } repository.updateSession(completed) } } finally { @@ -127,7 +127,7 @@ class SessionTrackingService : Service() { } val session = repository.getSessionById(sessionId) - if (session == null || session.status != com.atridad.openclimb.data.model.SessionStatus.ACTIVE) { + if (session == null || session.status != com.atridad.ascently.data.model.SessionStatus.ACTIVE) { stopSessionTracking() break } @@ -175,7 +175,7 @@ class SessionTrackingService : Service() { val session = runBlocking { repository.getSessionById(sessionId) } - if (session == null || session.status != com.atridad.openclimb.data.model.SessionStatus.ACTIVE) { + if (session == null || session.status != com.atridad.ascently.data.model.SessionStatus.ACTIVE) { stopSessionTracking() return } diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/OpenClimbApp.kt b/android/app/src/main/java/com/atridad/ascently/ui/AscentlyApp.kt similarity index 92% rename from android/app/src/main/java/com/atridad/openclimb/ui/OpenClimbApp.kt rename to android/app/src/main/java/com/atridad/ascently/ui/AscentlyApp.kt index f2b4778..a26a88e 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/OpenClimbApp.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/AscentlyApp.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui +package com.atridad.ascently.ui import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -17,21 +17,21 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute -import com.atridad.openclimb.data.database.OpenClimbDatabase -import com.atridad.openclimb.data.repository.ClimbRepository -import com.atridad.openclimb.data.sync.SyncService -import com.atridad.openclimb.navigation.Screen -import com.atridad.openclimb.navigation.bottomNavigationItems -import com.atridad.openclimb.ui.components.NotificationPermissionDialog -import com.atridad.openclimb.ui.screens.* -import com.atridad.openclimb.ui.viewmodel.ClimbViewModel -import com.atridad.openclimb.ui.viewmodel.ClimbViewModelFactory -import com.atridad.openclimb.utils.AppShortcutManager -import com.atridad.openclimb.utils.NotificationPermissionUtils +import com.atridad.ascently.data.database.AscentlyDatabase +import com.atridad.ascently.data.repository.ClimbRepository +import com.atridad.ascently.data.sync.SyncService +import com.atridad.ascently.navigation.Screen +import com.atridad.ascently.navigation.bottomNavigationItems +import com.atridad.ascently.ui.components.NotificationPermissionDialog +import com.atridad.ascently.ui.screens.* +import com.atridad.ascently.ui.viewmodel.ClimbViewModel +import com.atridad.ascently.ui.viewmodel.ClimbViewModelFactory +import com.atridad.ascently.utils.AppShortcutManager +import com.atridad.ascently.utils.NotificationPermissionUtils @OptIn(ExperimentalMaterial3Api::class) @Composable -fun OpenClimbApp( +fun AscentlyApp( shortcutAction: String? = null, lastUsedGymId: String? = null, onShortcutActionProcessed: () -> Unit = {} @@ -39,9 +39,9 @@ fun OpenClimbApp( val navController = rememberNavController() val context = LocalContext.current - var lastUsedGym by remember { mutableStateOf(null) } + var lastUsedGym by remember { mutableStateOf(null) } - val database = remember { OpenClimbDatabase.getDatabase(context) } + val database = remember { AscentlyDatabase.getDatabase(context) } val repository = remember { ClimbRepository(database, context) } val syncService = remember { SyncService(context, repository) } val viewModel: ClimbViewModel = @@ -115,7 +115,7 @@ fun OpenClimbApp( LaunchedEffect(shortcutAction, activeSession, gyms, lastUsedGym) { if (shortcutAction == AppShortcutManager.ACTION_START_SESSION && gyms.isNotEmpty()) { android.util.Log.d( - "OpenClimbApp", + "AscentlyApp", "Processing shortcut action: activeSession=$activeSession, gyms.size=${gyms.size}, lastUsedGymId=$lastUsedGymId, lastUsedGym=${lastUsedGym?.name}" ) @@ -125,12 +125,12 @@ fun OpenClimbApp( context ) ) { - android.util.Log.d("OpenClimbApp", "Showing notification permission dialog") + android.util.Log.d("AscentlyApp", "Showing notification permission dialog") showNotificationPermissionDialog = true } else { if (gyms.size == 1) { android.util.Log.d( - "OpenClimbApp", + "AscentlyApp", "Starting session with single gym: ${gyms.first().name}" ) viewModel.startSession(context, gyms.first().id) @@ -141,13 +141,13 @@ fun OpenClimbApp( if (targetGym != null) { android.util.Log.d( - "OpenClimbApp", + "AscentlyApp", "Starting session with target gym: ${targetGym.name}" ) viewModel.startSession(context, targetGym.id) } else { android.util.Log.d( - "OpenClimbApp", + "AscentlyApp", "No target gym found, navigating to selection" ) navController.navigate(Screen.AddEditSession()) @@ -156,7 +156,7 @@ fun OpenClimbApp( } } else { android.util.Log.d( - "OpenClimbApp", + "AscentlyApp", "Active session already exists: ${activeSession?.id}" ) } @@ -168,7 +168,7 @@ fun OpenClimbApp( var fabConfig by remember { mutableStateOf(null) } Scaffold( - bottomBar = { OpenClimbBottomNavigation(navController = navController) }, + bottomBar = { AscentlyBottomNavigation(navController = navController) }, floatingActionButton = { fabConfig?.let { config -> FloatingActionButton( @@ -363,7 +363,7 @@ fun OpenClimbApp( } @Composable -fun OpenClimbBottomNavigation(navController: NavHostController) { +fun AscentlyBottomNavigation(navController: NavHostController) { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/components/ActiveSessionBanner.kt b/android/app/src/main/java/com/atridad/ascently/ui/components/ActiveSessionBanner.kt similarity index 95% rename from android/app/src/main/java/com/atridad/openclimb/ui/components/ActiveSessionBanner.kt rename to android/app/src/main/java/com/atridad/ascently/ui/components/ActiveSessionBanner.kt index 2a3e11e..c54e743 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/components/ActiveSessionBanner.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/components/ActiveSessionBanner.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.components +package com.atridad.ascently.ui.components import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -10,9 +10,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.atridad.openclimb.data.model.ClimbSession -import com.atridad.openclimb.data.model.Gym -import com.atridad.openclimb.ui.theme.CustomIcons +import com.atridad.ascently.data.model.ClimbSession +import com.atridad.ascently.data.model.Gym +import com.atridad.ascently.ui.theme.CustomIcons import kotlinx.coroutines.delay import java.time.LocalDateTime import java.time.temporal.ChronoUnit diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/components/BarChart.kt b/android/app/src/main/java/com/atridad/ascently/ui/components/BarChart.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/ui/components/BarChart.kt rename to android/app/src/main/java/com/atridad/ascently/ui/components/BarChart.kt index cf2f7d2..fdb0d60 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/components/BarChart.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/components/BarChart.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.components +package com.atridad.ascently.ui.components import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.Box diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/components/FullscreenImageViewer.kt b/android/app/src/main/java/com/atridad/ascently/ui/components/FullscreenImageViewer.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/ui/components/FullscreenImageViewer.kt rename to android/app/src/main/java/com/atridad/ascently/ui/components/FullscreenImageViewer.kt index 7be80c5..887d8eb 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/components/FullscreenImageViewer.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/components/FullscreenImageViewer.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.components +package com.atridad.ascently.ui.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/components/ImageDisplay.kt b/android/app/src/main/java/com/atridad/ascently/ui/components/ImageDisplay.kt similarity index 98% rename from android/app/src/main/java/com/atridad/openclimb/ui/components/ImageDisplay.kt rename to android/app/src/main/java/com/atridad/ascently/ui/components/ImageDisplay.kt index de190f4..0d983e0 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/components/ImageDisplay.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/components/ImageDisplay.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.components +package com.atridad.ascently.ui.components import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/components/ImagePicker.kt b/android/app/src/main/java/com/atridad/ascently/ui/components/ImagePicker.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/ui/components/ImagePicker.kt rename to android/app/src/main/java/com/atridad/ascently/ui/components/ImagePicker.kt index 41fdf53..3ff28ef 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/components/ImagePicker.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/components/ImagePicker.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.components +package com.atridad.ascently.ui.components import android.Manifest import android.content.pm.PackageManager @@ -25,7 +25,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import androidx.core.content.FileProvider -import com.atridad.openclimb.utils.ImageUtils +import com.atridad.ascently.utils.ImageUtils import java.io.File import java.text.SimpleDateFormat import java.util.* diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/components/LineChart.kt b/android/app/src/main/java/com/atridad/ascently/ui/components/LineChart.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/ui/components/LineChart.kt rename to android/app/src/main/java/com/atridad/ascently/ui/components/LineChart.kt index ac62798..c508b29 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/components/LineChart.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/components/LineChart.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.components +package com.atridad.ascently.ui.components import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.Box diff --git a/android/app/src/main/java/com/atridad/ascently/ui/components/NotificationPermissionDialog.kt b/android/app/src/main/java/com/atridad/ascently/ui/components/NotificationPermissionDialog.kt new file mode 100644 index 0000000..5d3f961 --- /dev/null +++ b/android/app/src/main/java/com/atridad/ascently/ui/components/NotificationPermissionDialog.kt @@ -0,0 +1,76 @@ +package com.atridad.ascently.ui.components + +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Notifications +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties + +@Composable +fun NotificationPermissionDialog(onDismiss: () -> Unit, onRequestPermission: () -> Unit) { + Dialog( + onDismissRequest = onDismiss, + properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false) + ) { + Card( + modifier = Modifier.fillMaxWidth().padding(16.dp), + shape = MaterialTheme.shapes.medium + ) { + Column( + modifier = Modifier.padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + imageVector = Icons.Default.Notifications, + contentDescription = "Notifications", + modifier = Modifier.size(48.dp), + tint = MaterialTheme.colorScheme.primary + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "Enable Notifications", + style = MaterialTheme.typography.headlineSmall, + fontWeight = MaterialTheme.typography.headlineSmall.fontWeight, + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.height(12.dp)) + + Text( + text = + "Ascently needs notification permission to show your active climbing session. This helps you track your progress and ensures the session doesn't get interrupted.", + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + Spacer(modifier = Modifier.height(24.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + TextButton(onClick = onDismiss, modifier = Modifier.weight(1f)) { + Text("Not Now") + } + + Button( + onClick = { + onRequestPermission() + onDismiss() + }, + modifier = Modifier.weight(1f) + ) { Text("Enable") } + } + } + } + } +} diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/components/OrientationAwareImage.kt b/android/app/src/main/java/com/atridad/ascently/ui/components/OrientationAwareImage.kt similarity index 98% rename from android/app/src/main/java/com/atridad/openclimb/ui/components/OrientationAwareImage.kt rename to android/app/src/main/java/com/atridad/ascently/ui/components/OrientationAwareImage.kt index 3d6f73d..cb0ce38 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/components/OrientationAwareImage.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/components/OrientationAwareImage.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.components +package com.atridad.ascently.ui.components import android.graphics.BitmapFactory import android.graphics.Matrix @@ -12,7 +12,7 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.exifinterface.media.ExifInterface -import com.atridad.openclimb.utils.ImageUtils +import com.atridad.ascently.utils.ImageUtils import java.io.File import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/components/SyncIndicator.kt b/android/app/src/main/java/com/atridad/ascently/ui/components/SyncIndicator.kt similarity index 97% rename from android/app/src/main/java/com/atridad/openclimb/ui/components/SyncIndicator.kt rename to android/app/src/main/java/com/atridad/ascently/ui/components/SyncIndicator.kt index 3403f15..f84bc0e 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/components/SyncIndicator.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/components/SyncIndicator.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.components +package com.atridad.ascently.ui.components import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/health/HealthConnectCard.kt b/android/app/src/main/java/com/atridad/ascently/ui/health/HealthConnectCard.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/ui/health/HealthConnectCard.kt rename to android/app/src/main/java/com/atridad/ascently/ui/health/HealthConnectCard.kt index 36797a4..086722f 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/health/HealthConnectCard.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/health/HealthConnectCard.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.health +package com.atridad.ascently.ui.health import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.layout.* @@ -13,7 +13,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.atridad.openclimb.data.health.HealthConnectManager +import com.atridad.ascently.data.health.HealthConnectManager import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/screens/AddEditScreens.kt b/android/app/src/main/java/com/atridad/ascently/ui/screens/AddEditScreens.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/ui/screens/AddEditScreens.kt rename to android/app/src/main/java/com/atridad/ascently/ui/screens/AddEditScreens.kt index 1b785d3..dc21a82 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/screens/AddEditScreens.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/screens/AddEditScreens.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.screens +package com.atridad.ascently.ui.screens import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -17,9 +17,9 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import com.atridad.openclimb.data.model.* -import com.atridad.openclimb.ui.components.ImagePicker -import com.atridad.openclimb.ui.viewmodel.ClimbViewModel +import com.atridad.ascently.data.model.* +import com.atridad.ascently.ui.components.ImagePicker +import com.atridad.ascently.ui.viewmodel.ClimbViewModel import java.time.LocalDateTime import kotlinx.coroutines.flow.first diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/screens/AnalyticsScreen.kt b/android/app/src/main/java/com/atridad/ascently/ui/screens/AnalyticsScreen.kt similarity index 95% rename from android/app/src/main/java/com/atridad/openclimb/ui/screens/AnalyticsScreen.kt rename to android/app/src/main/java/com/atridad/ascently/ui/screens/AnalyticsScreen.kt index bd092fa..964f647 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/screens/AnalyticsScreen.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/screens/AnalyticsScreen.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.screens +package com.atridad.ascently.ui.screens import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -9,14 +9,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.atridad.openclimb.R -import com.atridad.openclimb.data.model.AttemptResult -import com.atridad.openclimb.data.model.ClimbType -import com.atridad.openclimb.data.model.DifficultySystem -import com.atridad.openclimb.ui.components.BarChart -import com.atridad.openclimb.ui.components.BarChartDataPoint -import com.atridad.openclimb.ui.components.SyncIndicator -import com.atridad.openclimb.ui.viewmodel.ClimbViewModel +import com.atridad.ascently.R +import com.atridad.ascently.data.model.AttemptResult +import com.atridad.ascently.data.model.ClimbType +import com.atridad.ascently.data.model.DifficultySystem +import com.atridad.ascently.ui.components.BarChart +import com.atridad.ascently.ui.components.BarChartDataPoint +import com.atridad.ascently.ui.components.SyncIndicator +import com.atridad.ascently.ui.viewmodel.ClimbViewModel import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -39,7 +39,7 @@ fun AnalyticsScreen(viewModel: ClimbViewModel) { ) { Icon( painter = painterResource(id = R.drawable.ic_mountains), - contentDescription = "OpenClimb Logo", + contentDescription = "Ascently Logo", modifier = Modifier.size(32.dp), tint = MaterialTheme.colorScheme.primary ) @@ -200,7 +200,9 @@ fun GradeDistributionChartCard(gradeDistributionData: List, - problems: List, - attempts: List + sessions: List, + problems: List, + attempts: List ): List { if (sessions.isEmpty() || problems.isEmpty() || attempts.isEmpty()) { return emptyList() diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/screens/DetailScreens.kt b/android/app/src/main/java/com/atridad/ascently/ui/screens/DetailScreens.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/ui/screens/DetailScreens.kt rename to android/app/src/main/java/com/atridad/ascently/ui/screens/DetailScreens.kt index 9e32755..8370e7e 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/screens/DetailScreens.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/screens/DetailScreens.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.screens +package com.atridad.ascently.ui.screens import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.clickable @@ -31,11 +31,11 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.lifecycle.viewModelScope -import com.atridad.openclimb.data.model.* -import com.atridad.openclimb.ui.components.FullscreenImageViewer -import com.atridad.openclimb.ui.components.ImageDisplaySection -import com.atridad.openclimb.ui.theme.CustomIcons -import com.atridad.openclimb.ui.viewmodel.ClimbViewModel +import com.atridad.ascently.data.model.* +import com.atridad.ascently.ui.components.FullscreenImageViewer +import com.atridad.ascently.ui.components.ImageDisplaySection +import com.atridad.ascently.ui.theme.CustomIcons +import com.atridad.ascently.ui.viewmodel.ClimbViewModel import java.time.LocalDateTime import java.time.format.DateTimeFormatter import kotlinx.coroutines.flow.first diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/screens/GymsScreen.kt b/android/app/src/main/java/com/atridad/ascently/ui/screens/GymsScreen.kt similarity index 93% rename from android/app/src/main/java/com/atridad/openclimb/ui/screens/GymsScreen.kt rename to android/app/src/main/java/com/atridad/ascently/ui/screens/GymsScreen.kt index 706dc40..c889c62 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/screens/GymsScreen.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/screens/GymsScreen.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.screens +package com.atridad.ascently.ui.screens import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -10,10 +10,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.atridad.openclimb.R -import com.atridad.openclimb.data.model.Gym -import com.atridad.openclimb.ui.components.SyncIndicator -import com.atridad.openclimb.ui.viewmodel.ClimbViewModel +import com.atridad.ascently.R +import com.atridad.ascently.data.model.Gym +import com.atridad.ascently.ui.components.SyncIndicator +import com.atridad.ascently.ui.viewmodel.ClimbViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -28,7 +28,7 @@ fun GymsScreen(viewModel: ClimbViewModel, onNavigateToGymDetail: (String) -> Uni ) { Icon( painter = painterResource(id = R.drawable.ic_mountains), - contentDescription = "OpenClimb Logo", + contentDescription = "Ascently Logo", modifier = Modifier.size(32.dp), tint = MaterialTheme.colorScheme.primary ) diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/screens/ProblemsScreen.kt b/android/app/src/main/java/com/atridad/ascently/ui/screens/ProblemsScreen.kt similarity index 96% rename from android/app/src/main/java/com/atridad/openclimb/ui/screens/ProblemsScreen.kt rename to android/app/src/main/java/com/atridad/ascently/ui/screens/ProblemsScreen.kt index 90d20b0..e97033c 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/screens/ProblemsScreen.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/screens/ProblemsScreen.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.screens +package com.atridad.ascently.ui.screens import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -15,14 +15,14 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.atridad.openclimb.R -import com.atridad.openclimb.data.model.Attempt -import com.atridad.openclimb.data.model.AttemptResult -import com.atridad.openclimb.data.model.ClimbType -import com.atridad.openclimb.data.model.Gym -import com.atridad.openclimb.data.model.Problem -import com.atridad.openclimb.ui.components.SyncIndicator -import com.atridad.openclimb.ui.viewmodel.ClimbViewModel +import com.atridad.ascently.R +import com.atridad.ascently.data.model.Attempt +import com.atridad.ascently.data.model.AttemptResult +import com.atridad.ascently.data.model.ClimbType +import com.atridad.ascently.data.model.Gym +import com.atridad.ascently.data.model.Problem +import com.atridad.ascently.ui.components.SyncIndicator +import com.atridad.ascently.ui.viewmodel.ClimbViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -57,7 +57,7 @@ fun ProblemsScreen(viewModel: ClimbViewModel, onNavigateToProblemDetail: (String ) { Icon( painter = painterResource(id = R.drawable.ic_mountains), - contentDescription = "OpenClimb Logo", + contentDescription = "Ascently Logo", modifier = Modifier.size(32.dp), tint = MaterialTheme.colorScheme.primary ) diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/screens/SessionsScreen.kt b/android/app/src/main/java/com/atridad/ascently/ui/screens/SessionsScreen.kt similarity index 95% rename from android/app/src/main/java/com/atridad/openclimb/ui/screens/SessionsScreen.kt rename to android/app/src/main/java/com/atridad/ascently/ui/screens/SessionsScreen.kt index 4248b27..f250ee5 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/screens/SessionsScreen.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/screens/SessionsScreen.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.screens +package com.atridad.ascently.ui.screens import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -16,12 +16,12 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.atridad.openclimb.R -import com.atridad.openclimb.data.model.ClimbSession -import com.atridad.openclimb.data.model.SessionStatus -import com.atridad.openclimb.ui.components.ActiveSessionBanner -import com.atridad.openclimb.ui.components.SyncIndicator -import com.atridad.openclimb.ui.viewmodel.ClimbViewModel +import com.atridad.ascently.R +import com.atridad.ascently.data.model.ClimbSession +import com.atridad.ascently.data.model.SessionStatus +import com.atridad.ascently.ui.components.ActiveSessionBanner +import com.atridad.ascently.ui.components.SyncIndicator +import com.atridad.ascently.ui.viewmodel.ClimbViewModel import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -46,7 +46,7 @@ fun SessionsScreen(viewModel: ClimbViewModel, onNavigateToSessionDetail: (String ) { Icon( painter = painterResource(id = R.drawable.ic_mountains), - contentDescription = "OpenClimb Logo", + contentDescription = "Ascently Logo", modifier = Modifier.size(32.dp), tint = MaterialTheme.colorScheme.primary ) diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/screens/SettingsScreen.kt b/android/app/src/main/java/com/atridad/ascently/ui/screens/SettingsScreen.kt similarity index 98% rename from android/app/src/main/java/com/atridad/openclimb/ui/screens/SettingsScreen.kt rename to android/app/src/main/java/com/atridad/ascently/ui/screens/SettingsScreen.kt index 8d9e0ea..05420fc 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/screens/SettingsScreen.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/screens/SettingsScreen.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.screens +package com.atridad.ascently.ui.screens import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -15,10 +15,10 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.atridad.openclimb.R -import com.atridad.openclimb.ui.components.SyncIndicator -import com.atridad.openclimb.ui.health.HealthConnectCard -import com.atridad.openclimb.ui.viewmodel.ClimbViewModel +import com.atridad.ascently.R +import com.atridad.ascently.ui.components.SyncIndicator +import com.atridad.ascently.ui.health.HealthConnectCard +import com.atridad.ascently.ui.viewmodel.ClimbViewModel import java.io.File import java.time.Instant import kotlinx.coroutines.launch @@ -86,7 +86,7 @@ fun SettingsScreen(viewModel: ClimbViewModel) { // Only allow ZIP files if (!fileName.lowercase().endsWith(".zip")) { viewModel.setError( - "Only ZIP files are supported for import. Please use a ZIP file exported from OpenClimb." + "Only ZIP files are supported for import. Please use a ZIP file exported from Ascently." ) return@let } @@ -129,7 +129,7 @@ fun SettingsScreen(viewModel: ClimbViewModel) { ) { Icon( painter = painterResource(id = R.drawable.ic_mountains), - contentDescription = "OpenClimb Logo", + contentDescription = "Ascently Logo", modifier = Modifier.size(32.dp), tint = MaterialTheme.colorScheme.primary ) @@ -336,7 +336,7 @@ fun SettingsScreen(viewModel: ClimbViewModel) { ListItem( headlineContent = { Text("Setup Sync") }, supportingContent = { - Text("Connect to your OpenClimb sync server") + Text("Connect to your Ascently sync server") }, leadingContent = { Icon(Icons.Default.CloudSync, contentDescription = null) @@ -421,7 +421,7 @@ fun SettingsScreen(viewModel: ClimbViewModel) { TextButton( onClick = { val defaultFileName = - "openclimb_export_${ + "ascently_export_${ java.time.LocalDateTime.now() .toString() .replace(":", "-") @@ -604,11 +604,11 @@ fun SettingsScreen(viewModel: ClimbViewModel) { painterResource( id = R.drawable.ic_mountains ), - contentDescription = "OpenClimb Logo", + contentDescription = "Ascently Logo", modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.primary ) - Text("OpenClimb") + Text("Ascently") } }, supportingContent = { Text("Track your climbing progress") }, diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/theme/Color.kt b/android/app/src/main/java/com/atridad/ascently/ui/theme/Color.kt similarity index 98% rename from android/app/src/main/java/com/atridad/openclimb/ui/theme/Color.kt rename to android/app/src/main/java/com/atridad/ascently/ui/theme/Color.kt index 989ace4..06a7993 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/theme/Color.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/theme/Color.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.theme +package com.atridad.ascently.ui.theme import androidx.compose.ui.graphics.Color diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/theme/CustomIcons.kt b/android/app/src/main/java/com/atridad/ascently/ui/theme/CustomIcons.kt similarity index 94% rename from android/app/src/main/java/com/atridad/openclimb/ui/theme/CustomIcons.kt rename to android/app/src/main/java/com/atridad/ascently/ui/theme/CustomIcons.kt index 73936f7..ba536ef 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/theme/CustomIcons.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/theme/CustomIcons.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.theme +package com.atridad.ascently.ui.theme import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/theme/Theme.kt b/android/app/src/main/java/com/atridad/ascently/ui/theme/Theme.kt similarity index 98% rename from android/app/src/main/java/com/atridad/openclimb/ui/theme/Theme.kt rename to android/app/src/main/java/com/atridad/ascently/ui/theme/Theme.kt index 1a8ecf8..2c137da 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/theme/Theme.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/theme/Theme.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.theme +package com.atridad.ascently.ui.theme import android.app.Activity import androidx.compose.foundation.isSystemInDarkTheme @@ -88,7 +88,7 @@ private val LightColorScheme = lightColorScheme( ) @Composable -fun OpenClimbTheme( +fun AscentlyTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ and provides full Material You theming // When enabled, it adapts to the user's system wallpaper colors diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/theme/Type.kt b/android/app/src/main/java/com/atridad/ascently/ui/theme/Type.kt similarity index 92% rename from android/app/src/main/java/com/atridad/openclimb/ui/theme/Type.kt rename to android/app/src/main/java/com/atridad/ascently/ui/theme/Type.kt index 38ce75a..faa4bbb 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/theme/Type.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/theme/Type.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.ui.theme +package com.atridad.ascently.ui.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/viewmodel/ClimbViewModel.kt b/android/app/src/main/java/com/atridad/ascently/ui/viewmodel/ClimbViewModel.kt similarity index 96% rename from android/app/src/main/java/com/atridad/openclimb/ui/viewmodel/ClimbViewModel.kt rename to android/app/src/main/java/com/atridad/ascently/ui/viewmodel/ClimbViewModel.kt index cc24c84..361801c 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/viewmodel/ClimbViewModel.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/viewmodel/ClimbViewModel.kt @@ -1,17 +1,17 @@ -package com.atridad.openclimb.ui.viewmodel +package com.atridad.ascently.ui.viewmodel import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.atridad.openclimb.data.health.HealthConnectManager -import com.atridad.openclimb.data.model.* -import com.atridad.openclimb.data.repository.ClimbRepository -import com.atridad.openclimb.data.sync.SyncService -import com.atridad.openclimb.service.SessionTrackingService -import com.atridad.openclimb.utils.ImageNamingUtils -import com.atridad.openclimb.utils.ImageUtils -import com.atridad.openclimb.utils.SessionShareUtils -import com.atridad.openclimb.widget.ClimbStatsWidgetProvider +import com.atridad.ascently.data.health.HealthConnectManager +import com.atridad.ascently.data.model.* +import com.atridad.ascently.data.repository.ClimbRepository +import com.atridad.ascently.data.sync.SyncService +import com.atridad.ascently.service.SessionTrackingService +import com.atridad.ascently.utils.ImageNamingUtils +import com.atridad.ascently.utils.ImageUtils +import com.atridad.ascently.utils.SessionShareUtils +import com.atridad.ascently.widget.ClimbStatsWidgetProvider import java.io.File import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* @@ -260,7 +260,7 @@ class ClimbViewModel( viewModelScope.launch { android.util.Log.d("ClimbViewModel", "startSession called with gymId: $gymId") - if (!com.atridad.openclimb.utils.NotificationPermissionUtils + if (!com.atridad.ascently.utils.NotificationPermissionUtils .isNotificationPermissionGranted(context) ) { android.util.Log.d("ClimbViewModel", "Notification permission not granted") @@ -305,7 +305,7 @@ class ClimbViewModel( fun endSession(context: Context, sessionId: String) { viewModelScope.launch { - if (!com.atridad.openclimb.utils.NotificationPermissionUtils + if (!com.atridad.ascently.utils.NotificationPermissionUtils .isNotificationPermissionGranted(context) ) { _uiState.value = @@ -416,7 +416,7 @@ class ClimbViewModel( if (!file.name.lowercase().endsWith(".zip")) { throw Exception( - "Only ZIP files are supported for import. Please use a ZIP file exported from OpenClimb." + "Only ZIP files are supported for import. Please use a ZIP file exported from Ascently." ) } diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/viewmodel/ClimbViewModelFactory.kt b/android/app/src/main/java/com/atridad/ascently/ui/viewmodel/ClimbViewModelFactory.kt similarity index 80% rename from android/app/src/main/java/com/atridad/openclimb/ui/viewmodel/ClimbViewModelFactory.kt rename to android/app/src/main/java/com/atridad/ascently/ui/viewmodel/ClimbViewModelFactory.kt index 5a96ecf..237048f 100644 --- a/android/app/src/main/java/com/atridad/openclimb/ui/viewmodel/ClimbViewModelFactory.kt +++ b/android/app/src/main/java/com/atridad/ascently/ui/viewmodel/ClimbViewModelFactory.kt @@ -1,10 +1,10 @@ -package com.atridad.openclimb.ui.viewmodel +package com.atridad.ascently.ui.viewmodel import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import com.atridad.openclimb.data.repository.ClimbRepository -import com.atridad.openclimb.data.sync.SyncService +import com.atridad.ascently.data.repository.ClimbRepository +import com.atridad.ascently.data.sync.SyncService class ClimbViewModelFactory( private val repository: ClimbRepository, diff --git a/android/app/src/main/java/com/atridad/openclimb/utils/DateFormatUtils.kt b/android/app/src/main/java/com/atridad/ascently/utils/DateFormatUtils.kt similarity index 97% rename from android/app/src/main/java/com/atridad/openclimb/utils/DateFormatUtils.kt rename to android/app/src/main/java/com/atridad/ascently/utils/DateFormatUtils.kt index 17058fe..2c32fca 100644 --- a/android/app/src/main/java/com/atridad/openclimb/utils/DateFormatUtils.kt +++ b/android/app/src/main/java/com/atridad/ascently/utils/DateFormatUtils.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.utils +package com.atridad.ascently.utils import java.time.Instant import java.time.ZoneOffset diff --git a/android/app/src/main/java/com/atridad/openclimb/utils/ImageNamingUtils.kt b/android/app/src/main/java/com/atridad/ascently/utils/ImageNamingUtils.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/utils/ImageNamingUtils.kt rename to android/app/src/main/java/com/atridad/ascently/utils/ImageNamingUtils.kt index 5aabe5b..dfdf41f 100644 --- a/android/app/src/main/java/com/atridad/openclimb/utils/ImageNamingUtils.kt +++ b/android/app/src/main/java/com/atridad/ascently/utils/ImageNamingUtils.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.utils +package com.atridad.ascently.utils import java.security.MessageDigest import java.util.* diff --git a/android/app/src/main/java/com/atridad/openclimb/utils/ImageUtils.kt b/android/app/src/main/java/com/atridad/ascently/utils/ImageUtils.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/utils/ImageUtils.kt rename to android/app/src/main/java/com/atridad/ascently/utils/ImageUtils.kt index 9a8ae77..2a2d74f 100644 --- a/android/app/src/main/java/com/atridad/openclimb/utils/ImageUtils.kt +++ b/android/app/src/main/java/com/atridad/ascently/utils/ImageUtils.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.utils +package com.atridad.ascently.utils import android.annotation.SuppressLint import android.content.Context diff --git a/android/app/src/main/java/com/atridad/ascently/utils/MigrationManager.kt b/android/app/src/main/java/com/atridad/ascently/utils/MigrationManager.kt new file mode 100644 index 0000000..7a30c0a --- /dev/null +++ b/android/app/src/main/java/com/atridad/ascently/utils/MigrationManager.kt @@ -0,0 +1,175 @@ +package com.atridad.ascently.utils + +import android.content.Context +import android.content.SharedPreferences +import android.util.Log +import androidx.core.content.edit + +/** + * Handles migration of data from OpenClimb to Ascently This includes SharedPreferences, database + * names, and other local storage + */ +class MigrationManager(private val context: Context) { + + companion object { + private const val TAG = "MigrationManager" + private const val MIGRATION_PREFS = "ascently_migration_state" + private const val MIGRATION_COMPLETED_KEY = "openclimb_to_ascently_completed" + } + + private val migrationPrefs: SharedPreferences = + context.getSharedPreferences(MIGRATION_PREFS, Context.MODE_PRIVATE) + + /** + * Perform migration from OpenClimb to Ascently if needed This should be called early in app + * startup + */ + fun migrateIfNeeded() { + if (migrationPrefs.getBoolean(MIGRATION_COMPLETED_KEY, false)) { + Log.d(TAG, "Migration already completed, skipping") + return + } + + Log.i(TAG, "🔄 Starting migration from OpenClimb to Ascently...") + var migrationCount = 0 + + // Migrate SharedPreferences + migrationCount += migrateSharedPreferences() + + // Mark migration as completed + migrationPrefs.edit { putBoolean(MIGRATION_COMPLETED_KEY, true) } + + if (migrationCount > 0) { + Log.i( + TAG, + "🎉 Migration completed! Migrated $migrationCount items from OpenClimb to Ascently" + ) + } else { + Log.i(TAG, "ℹ️ No OpenClimb data found to migrate") + } + } + + /** Migrate SharedPreferences from OpenClimb naming to Ascently naming */ + private fun migrateSharedPreferences(): Int { + var count = 0 + + // Define preference file migrations + val preferenceFileMigrations = + listOf( + "openclimb_data_state" to "ascently_data_state", + "health_connect_prefs" to "health_connect_prefs", // Keep same name + "deleted_items" to "deleted_items", // Keep same name + "sync_preferences" to "sync_preferences" // Keep same name + ) + + for ((oldFileName, newFileName) in preferenceFileMigrations) { + if (oldFileName != newFileName) { + count += migratePreferenceFile(oldFileName, newFileName) + } + } + + // Migrate specific keys within preference files + count += migratePreferenceKeys() + + return count + } + + /** Migrate an entire SharedPreferences file */ + private fun migratePreferenceFile(oldFileName: String, newFileName: String): Int { + val oldPrefs = context.getSharedPreferences(oldFileName, Context.MODE_PRIVATE) + val newPrefs = context.getSharedPreferences(newFileName, Context.MODE_PRIVATE) + + // If old prefs exist and new prefs are empty, migrate + if (oldPrefs.all.isNotEmpty() && newPrefs.all.isEmpty()) { + newPrefs.edit { + oldPrefs.all.forEach { (key, value) -> + when (value) { + is String -> putString(key, value) + is Int -> putInt(key, value) + is Long -> putLong(key, value) + is Float -> putFloat(key, value) + is Boolean -> putBoolean(key, value) + is Set<*> -> { + @Suppress("UNCHECKED_CAST") putStringSet(key, value as Set) + } + } + } + } + + // Clear old preferences + oldPrefs.edit { clear() } + + Log.d( + TAG, + "✅ Migrated preference file: $oldFileName → $newFileName (${oldPrefs.all.size} keys)" + ) + return oldPrefs.all.size + } + + return 0 + } + + /** Migrate specific keys within preference files that might contain openclimb references */ + private fun migratePreferenceKeys(): Int { + var count = 0 + + // Check for any openclimb-prefixed keys across all preference files + val preferencesToCheck = + listOf( + "ascently_data_state", + "health_connect_prefs", + "deleted_items", + "sync_preferences" + ) + + for (prefFileName in preferencesToCheck) { + val prefs = context.getSharedPreferences(prefFileName, Context.MODE_PRIVATE) + val keysToMigrate = mutableListOf>() + + // Find keys that start with openclimb_ and should be ascently_ + prefs.all.keys.forEach { key -> + if (key.startsWith("openclimb_")) { + val newKey = key.replace("openclimb_", "ascently_") + keysToMigrate.add(key to newKey) + } + } + + // Perform the key migrations + if (keysToMigrate.isNotEmpty()) { + prefs.edit { + keysToMigrate.forEach { (oldKey, newKey) -> + val value = prefs.all[oldKey] + when (value) { + is String -> putString(newKey, value) + is Int -> putInt(newKey, value) + is Long -> putLong(newKey, value) + is Float -> putFloat(newKey, value) + is Boolean -> putBoolean(newKey, value) + is Set<*> -> { + @Suppress("UNCHECKED_CAST") + putStringSet(newKey, value as Set) + } + } + remove(oldKey) + } + } + + Log.d(TAG, "✅ Migrated ${keysToMigrate.size} keys in $prefFileName") + count += keysToMigrate.size + } + } + + return count + } + + /** Check if migration has been completed */ + fun isMigrationCompleted(): Boolean { + return migrationPrefs.getBoolean(MIGRATION_COMPLETED_KEY, false) + } + + /** Reset migration state (for testing purposes) */ + fun resetMigrationState() { + migrationPrefs.edit { putBoolean(MIGRATION_COMPLETED_KEY, false) } + Log.d(TAG, "Migration state reset") + } +} diff --git a/android/app/src/main/java/com/atridad/openclimb/utils/NotificationPermissionUtils.kt b/android/app/src/main/java/com/atridad/ascently/utils/NotificationPermissionUtils.kt similarity index 95% rename from android/app/src/main/java/com/atridad/openclimb/utils/NotificationPermissionUtils.kt rename to android/app/src/main/java/com/atridad/ascently/utils/NotificationPermissionUtils.kt index 318dec5..50b4c7f 100644 --- a/android/app/src/main/java/com/atridad/openclimb/utils/NotificationPermissionUtils.kt +++ b/android/app/src/main/java/com/atridad/ascently/utils/NotificationPermissionUtils.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.utils +package com.atridad.ascently.utils import android.Manifest import android.content.Context diff --git a/android/app/src/main/java/com/atridad/openclimb/utils/SessionShareUtils.kt b/android/app/src/main/java/com/atridad/ascently/utils/SessionShareUtils.kt similarity index 99% rename from android/app/src/main/java/com/atridad/openclimb/utils/SessionShareUtils.kt rename to android/app/src/main/java/com/atridad/ascently/utils/SessionShareUtils.kt index 711da13..789a312 100644 --- a/android/app/src/main/java/com/atridad/openclimb/utils/SessionShareUtils.kt +++ b/android/app/src/main/java/com/atridad/ascently/utils/SessionShareUtils.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.utils +package com.atridad.ascently.utils import android.content.Context import android.content.Intent @@ -7,7 +7,7 @@ import android.graphics.drawable.GradientDrawable import androidx.core.content.FileProvider import androidx.core.graphics.createBitmap import androidx.core.graphics.toColorInt -import com.atridad.openclimb.data.model.* +import com.atridad.ascently.data.model.* import java.io.File import java.io.FileOutputStream import java.time.LocalDateTime @@ -382,7 +382,7 @@ object SessionShareUtils { isAntiAlias = true textAlign = Paint.Align.CENTER } - canvas.drawText("OpenClimb", width / 2f, height - 40f, brandingPaint) + canvas.drawText("Ascently", width / 2f, height - 40f, brandingPaint) // Save to file val shareDir = File(context.cacheDir, "shares") @@ -481,7 +481,7 @@ object SessionShareUtils { action = Intent.ACTION_SEND type = "image/png" putExtra(Intent.EXTRA_STREAM, uri) - putExtra(Intent.EXTRA_TEXT, "Check out my climbing session! #OpenClimb") + putExtra(Intent.EXTRA_TEXT, "Check out my climbing session! #Ascently") addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } diff --git a/android/app/src/main/java/com/atridad/openclimb/utils/ShortcutManager.kt b/android/app/src/main/java/com/atridad/ascently/utils/ShortcutManager.kt similarity index 90% rename from android/app/src/main/java/com/atridad/openclimb/utils/ShortcutManager.kt rename to android/app/src/main/java/com/atridad/ascently/utils/ShortcutManager.kt index 02052ac..94b64de 100644 --- a/android/app/src/main/java/com/atridad/openclimb/utils/ShortcutManager.kt +++ b/android/app/src/main/java/com/atridad/ascently/utils/ShortcutManager.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.utils +package com.atridad.ascently.utils import android.content.Context import android.content.Intent @@ -7,23 +7,23 @@ import android.content.pm.ShortcutManager import android.graphics.drawable.Icon import android.os.Build import androidx.annotation.RequiresApi -import com.atridad.openclimb.MainActivity -import com.atridad.openclimb.R +import com.atridad.ascently.MainActivity +import com.atridad.ascently.R object AppShortcutManager { const val SHORTCUT_START_SESSION = "start_session" const val SHORTCUT_END_SESSION = "end_session" - const val ACTION_START_SESSION = "com.atridad.openclimb.action.START_SESSION" - const val ACTION_END_SESSION = "com.atridad.openclimb.action.END_SESSION" + const val ACTION_START_SESSION = "com.atridad.ascently.action.START_SESSION" + const val ACTION_END_SESSION = "com.atridad.ascently.action.END_SESSION" /** Updates the app shortcuts based on current session state */ fun updateShortcuts( context: Context, hasActiveSession: Boolean, hasGyms: Boolean, - lastUsedGym: com.atridad.openclimb.data.model.Gym? = null + lastUsedGym: com.atridad.ascently.data.model.Gym? = null ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { val shortcutManager = context.getSystemService(ShortcutManager::class.java) @@ -45,7 +45,7 @@ object AppShortcutManager { @RequiresApi(Build.VERSION_CODES.N_MR1) private fun createStartSessionShortcut( context: Context, - lastUsedGym: com.atridad.openclimb.data.model.Gym? = null + lastUsedGym: com.atridad.ascently.data.model.Gym? = null ): ShortcutInfo { val startIntent = Intent(context, MainActivity::class.java).apply { diff --git a/android/app/src/main/java/com/atridad/openclimb/utils/ZipExportImportUtils.kt b/android/app/src/main/java/com/atridad/ascently/utils/ZipExportImportUtils.kt similarity index 97% rename from android/app/src/main/java/com/atridad/openclimb/utils/ZipExportImportUtils.kt rename to android/app/src/main/java/com/atridad/ascently/utils/ZipExportImportUtils.kt index 6456606..9db6c8b 100644 --- a/android/app/src/main/java/com/atridad/openclimb/utils/ZipExportImportUtils.kt +++ b/android/app/src/main/java/com/atridad/ascently/utils/ZipExportImportUtils.kt @@ -1,8 +1,8 @@ -package com.atridad.openclimb.utils +package com.atridad.ascently.utils import android.content.Context -import com.atridad.openclimb.data.format.BackupProblem -import com.atridad.openclimb.data.format.ClimbDataBackup +import com.atridad.ascently.data.format.BackupProblem +import com.atridad.ascently.data.format.ClimbDataBackup import java.io.File import java.io.FileInputStream import java.io.FileOutputStream @@ -33,14 +33,14 @@ object ZipExportImportUtils { context.getExternalFilesDir( android.os.Environment.DIRECTORY_DOCUMENTS ), - "OpenClimb" + "Ascently" ) if (!exportDir.exists()) { exportDir.mkdirs() } val timestamp = LocalDateTime.now().toString().replace(":", "-").replace(".", "-") - val zipFile = File(exportDir, "openclimb_export_$timestamp.zip") + val zipFile = File(exportDir, "ascently_export_$timestamp.zip") try { ZipOutputStream(FileOutputStream(zipFile)).use { zipOut -> @@ -182,8 +182,8 @@ object ZipExportImportUtils { referencedImagePaths: Set ): String { return buildString { - appendLine("OpenClimb Export Metadata") - appendLine("=======================") + appendLine("Ascently Export Metadata") + appendLine("========================") appendLine("Export Date: ${exportData.exportedAt}") appendLine("Version: ${exportData.version}") appendLine("Gyms: ${exportData.gyms.size}") diff --git a/android/app/src/main/java/com/atridad/openclimb/widget/ClimbStatsWidgetProvider.kt b/android/app/src/main/java/com/atridad/ascently/widget/ClimbStatsWidgetProvider.kt similarity index 94% rename from android/app/src/main/java/com/atridad/openclimb/widget/ClimbStatsWidgetProvider.kt rename to android/app/src/main/java/com/atridad/ascently/widget/ClimbStatsWidgetProvider.kt index beee244..52e27d0 100644 --- a/android/app/src/main/java/com/atridad/openclimb/widget/ClimbStatsWidgetProvider.kt +++ b/android/app/src/main/java/com/atridad/ascently/widget/ClimbStatsWidgetProvider.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb.widget +package com.atridad.ascently.widget import android.app.PendingIntent import android.appwidget.AppWidgetManager @@ -7,10 +7,10 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.widget.RemoteViews -import com.atridad.openclimb.MainActivity -import com.atridad.openclimb.R -import com.atridad.openclimb.data.database.OpenClimbDatabase -import com.atridad.openclimb.data.repository.ClimbRepository +import com.atridad.ascently.MainActivity +import com.atridad.ascently.R +import com.atridad.ascently.data.database.AscentlyDatabase +import com.atridad.ascently.data.repository.ClimbRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -45,7 +45,7 @@ class ClimbStatsWidgetProvider : AppWidgetProvider() { ) { coroutineScope.launch { try { - val database = OpenClimbDatabase.getDatabase(context) + val database = AscentlyDatabase.getDatabase(context) val repository = ClimbRepository(database, context) // Fetch stats data @@ -65,10 +65,10 @@ class ClimbStatsWidgetProvider : AppWidgetProvider() { attempts.any { attempt -> attempt.problemId == problem.id && (attempt.result == - com.atridad.openclimb.data.model + com.atridad.ascently.data.model .AttemptResult.SUCCESS || attempt.result == - com.atridad.openclimb.data.model + com.atridad.ascently.data.model .AttemptResult.FLASH) } } diff --git a/android/app/src/main/java/com/atridad/openclimb/ui/components/NotificationPermissionDialog.kt b/android/app/src/main/java/com/atridad/openclimb/ui/components/NotificationPermissionDialog.kt deleted file mode 100644 index 9b463ea..0000000 --- a/android/app/src/main/java/com/atridad/openclimb/ui/components/NotificationPermissionDialog.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.atridad.openclimb.ui.components - -import androidx.compose.foundation.layout.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Notifications -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties - -@Composable -fun NotificationPermissionDialog( - onDismiss: () -> Unit, - onRequestPermission: () -> Unit -) { - Dialog( - onDismissRequest = onDismiss, - properties = DialogProperties( - dismissOnBackPress = false, - dismissOnClickOutside = false - ) - ) { - Card( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - shape = MaterialTheme.shapes.medium - ) { - Column( - modifier = Modifier.padding(24.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Icon( - imageVector = Icons.Default.Notifications, - contentDescription = "Notifications", - modifier = Modifier.size(48.dp), - tint = MaterialTheme.colorScheme.primary - ) - - Spacer(modifier = Modifier.height(16.dp)) - - Text( - text = "Enable Notifications", - style = MaterialTheme.typography.headlineSmall, - fontWeight = MaterialTheme.typography.headlineSmall.fontWeight, - textAlign = TextAlign.Center - ) - - Spacer(modifier = Modifier.height(12.dp)) - - Text( - text = "OpenClimb needs notification permission to show your active climbing session. This helps you track your progress and ensures the session doesn't get interrupted.", - style = MaterialTheme.typography.bodyMedium, - textAlign = TextAlign.Center, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - - Spacer(modifier = Modifier.height(24.dp)) - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp) - ) { - TextButton( - onClick = onDismiss, - modifier = Modifier.weight(1f) - ) { - Text("Not Now") - } - - Button( - onClick = { - onRequestPermission() - onDismiss() - }, - modifier = Modifier.weight(1f) - ) { - Text("Enable") - } - } - } - } - } -} diff --git a/android/app/src/main/res/layout/widget_climb_stats.xml b/android/app/src/main/res/layout/widget_climb_stats.xml index b8ba2cf..c36d38d 100644 --- a/android/app/src/main/res/layout/widget_climb_stats.xml +++ b/android/app/src/main/res/layout/widget_climb_stats.xml @@ -26,7 +26,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:text="OpenClimb" + android:text="Ascently" android:textSize="16sp" android:textStyle="bold" android:textColor="@color/widget_text_primary" /> diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index b600281..8434f4c 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - OpenClimb + Ascently Tracks active climbing sessions and displays session information in the notification area diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml index c8ca7f1..39e68ea 100644 --- a/android/app/src/main/res/values/themes.xml +++ b/android/app/src/main/res/values/themes.xml @@ -1,10 +1,10 @@ - - \ No newline at end of file + diff --git a/android/app/src/test/java/com/atridad/openclimb/BusinessLogicTests.kt b/android/app/src/test/java/com/atridad/openclimb/BusinessLogicTests.kt index 7c2352d..120438e 100644 --- a/android/app/src/test/java/com/atridad/openclimb/BusinessLogicTests.kt +++ b/android/app/src/test/java/com/atridad/openclimb/BusinessLogicTests.kt @@ -1,7 +1,7 @@ -package com.atridad.openclimb +package com.atridad.ascently -import com.atridad.openclimb.data.format.* -import com.atridad.openclimb.data.model.* +import com.atridad.ascently.data.format.* +import com.atridad.ascently.data.model.* import java.time.LocalDateTime import java.time.format.DateTimeFormatter import org.junit.Assert.* diff --git a/android/app/src/test/java/com/atridad/openclimb/DataModelTests.kt b/android/app/src/test/java/com/atridad/openclimb/DataModelTests.kt index 5c4b528..0fcda04 100644 --- a/android/app/src/test/java/com/atridad/openclimb/DataModelTests.kt +++ b/android/app/src/test/java/com/atridad/openclimb/DataModelTests.kt @@ -1,7 +1,7 @@ -package com.atridad.openclimb +package com.atridad.ascently -import com.atridad.openclimb.data.format.* -import com.atridad.openclimb.data.model.* +import com.atridad.ascently.data.format.* +import com.atridad.ascently.data.model.* import java.time.Instant import java.time.format.DateTimeFormatter import org.junit.Assert.* diff --git a/android/app/src/test/java/com/atridad/openclimb/SyncMergeLogicTest.kt b/android/app/src/test/java/com/atridad/openclimb/SyncMergeLogicTest.kt index e73c549..f80fd7a 100644 --- a/android/app/src/test/java/com/atridad/openclimb/SyncMergeLogicTest.kt +++ b/android/app/src/test/java/com/atridad/openclimb/SyncMergeLogicTest.kt @@ -1,7 +1,7 @@ -package com.atridad.openclimb +package com.atridad.ascently -import com.atridad.openclimb.data.format.* -import com.atridad.openclimb.data.model.* +import com.atridad.ascently.data.format.* +import com.atridad.ascently.data.model.* import org.junit.Assert.* import org.junit.Test diff --git a/android/app/src/test/java/com/atridad/openclimb/UtilityTests.kt b/android/app/src/test/java/com/atridad/openclimb/UtilityTests.kt index 58cc1f4..5f9395c 100644 --- a/android/app/src/test/java/com/atridad/openclimb/UtilityTests.kt +++ b/android/app/src/test/java/com/atridad/openclimb/UtilityTests.kt @@ -1,4 +1,4 @@ -package com.atridad.openclimb +package com.atridad.ascently import java.time.LocalDateTime import java.time.format.DateTimeFormatter diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index 8d5dd2f..a0441d2 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -11,6 +11,7 @@ pluginManagement { gradlePluginPortal() } } + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { @@ -20,5 +21,6 @@ dependencyResolutionManagement { } } -rootProject.name = "OpenClimb" +rootProject.name = "Ascently" + include(":app") diff --git a/ios/OpenClimb.xcodeproj/LiveActivityManager.swift b/ios/Ascently.xcodeproj/LiveActivityManager.swift similarity index 100% rename from ios/OpenClimb.xcodeproj/LiveActivityManager.swift rename to ios/Ascently.xcodeproj/LiveActivityManager.swift diff --git a/ios/OpenClimb.xcodeproj/project.pbxproj b/ios/Ascently.xcodeproj/project.pbxproj similarity index 87% rename from ios/OpenClimb.xcodeproj/project.pbxproj rename to ios/Ascently.xcodeproj/project.pbxproj index c9c880d..0f0651d 100644 --- a/ios/OpenClimb.xcodeproj/project.pbxproj +++ b/ios/Ascently.xcodeproj/project.pbxproj @@ -20,7 +20,7 @@ containerPortal = D24C19602E75002A0045894C /* Project object */; proxyType = 1; remoteGlobalIDString = D24C19672E75002A0045894C; - remoteInfo = OpenClimb; + remoteInfo = Ascently; }; D2FE949E2E78FEE1008CDB25 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -46,9 +46,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - D24C19682E75002A0045894C /* OpenClimb.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpenClimb.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D24C19682E75002A0045894C /* Ascently.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ascently.app; sourceTree = BUILT_PRODUCTS_DIR; }; D268B79E2E83894A003AA641 /* SessionStatusLiveExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SessionStatusLiveExtension.entitlements; sourceTree = ""; }; - D2F32FAD2E90B26500B1BC56 /* OpenClimbTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpenClimbTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D2F32FAD2E90B26500B1BC56 /* AscentlyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AscentlyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D2FE94802E78E958008CDB25 /* ActivityKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ActivityKit.framework; path = System/Library/Frameworks/ActivityKit.framework; sourceTree = SDKROOT; }; D2FE948B2E78FEE0008CDB25 /* SessionStatusLiveExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SessionStatusLiveExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; D2FE948C2E78FEE0008CDB25 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; @@ -56,12 +56,12 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - D28C3C8B2E75111D00F7AEE9 /* Exceptions for "OpenClimb" folder in "OpenClimb" target */ = { + D28C3C8B2E75111D00F7AEE9 /* Exceptions for "Ascently" folder in "Ascently" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( Info.plist, ); - target = D24C19672E75002A0045894C /* OpenClimb */; + target = D24C19672E75002A0045894C /* Ascently */; }; D2FE94A42E78FEE1008CDB25 /* Exceptions for "SessionStatusLive" folder in "SessionStatusLiveExtension" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; @@ -73,17 +73,17 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - D24C196A2E75002A0045894C /* OpenClimb */ = { + D24C196A2E75002A0045894C /* Ascently */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( - D28C3C8B2E75111D00F7AEE9 /* Exceptions for "OpenClimb" folder in "OpenClimb" target */, + D28C3C8B2E75111D00F7AEE9 /* Exceptions for "Ascently" folder in "Ascently" target */, ); - path = OpenClimb; + path = Ascently; sourceTree = ""; }; - D2F32FAE2E90B26500B1BC56 /* OpenClimbTests */ = { + D2F32FAE2E90B26500B1BC56 /* AscentlyTests */ = { isa = PBXFileSystemSynchronizedRootGroup; - path = OpenClimbTests; + path = AscentlyTests; sourceTree = ""; }; D2FE94902E78FEE0008CDB25 /* SessionStatusLive */ = { @@ -129,9 +129,9 @@ isa = PBXGroup; children = ( D268B79E2E83894A003AA641 /* SessionStatusLiveExtension.entitlements */, - D24C196A2E75002A0045894C /* OpenClimb */, + D24C196A2E75002A0045894C /* Ascently */, D2FE94902E78FEE0008CDB25 /* SessionStatusLive */, - D2F32FAE2E90B26500B1BC56 /* OpenClimbTests */, + D2F32FAE2E90B26500B1BC56 /* AscentlyTests */, D2FE947F2E78E958008CDB25 /* Frameworks */, D24C19692E75002A0045894C /* Products */, ); @@ -140,9 +140,9 @@ D24C19692E75002A0045894C /* Products */ = { isa = PBXGroup; children = ( - D24C19682E75002A0045894C /* OpenClimb.app */, + D24C19682E75002A0045894C /* Ascently.app */, D2FE948B2E78FEE0008CDB25 /* SessionStatusLiveExtension.appex */, - D2F32FAD2E90B26500B1BC56 /* OpenClimbTests.xctest */, + D2F32FAD2E90B26500B1BC56 /* AscentlyTests.xctest */, ); name = Products; sourceTree = ""; @@ -160,9 +160,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - D24C19672E75002A0045894C /* OpenClimb */ = { + D24C19672E75002A0045894C /* Ascently */ = { isa = PBXNativeTarget; - buildConfigurationList = D24C19732E75002A0045894C /* Build configuration list for PBXNativeTarget "OpenClimb" */; + buildConfigurationList = D24C19732E75002A0045894C /* Build configuration list for PBXNativeTarget "Ascently" */; buildPhases = ( D24C19642E75002A0045894C /* Sources */, D24C19652E75002A0045894C /* Frameworks */, @@ -175,18 +175,18 @@ D2FE949F2E78FEE1008CDB25 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( - D24C196A2E75002A0045894C /* OpenClimb */, + D24C196A2E75002A0045894C /* Ascently */, ); - name = OpenClimb; + name = Ascently; packageProductDependencies = ( ); - productName = OpenClimb; - productReference = D24C19682E75002A0045894C /* OpenClimb.app */; + productName = Ascently; + productReference = D24C19682E75002A0045894C /* Ascently.app */; productType = "com.apple.product-type.application"; }; - D2F32FAC2E90B26500B1BC56 /* OpenClimbTests */ = { + D2F32FAC2E90B26500B1BC56 /* AscentlyTests */ = { isa = PBXNativeTarget; - buildConfigurationList = D2F32FB52E90B26500B1BC56 /* Build configuration list for PBXNativeTarget "OpenClimbTests" */; + buildConfigurationList = D2F32FB52E90B26500B1BC56 /* Build configuration list for PBXNativeTarget "AscentlyTests" */; buildPhases = ( D2F32FA92E90B26500B1BC56 /* Sources */, D2F32FAA2E90B26500B1BC56 /* Frameworks */, @@ -198,13 +198,13 @@ D2F32FB22E90B26500B1BC56 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( - D2F32FAE2E90B26500B1BC56 /* OpenClimbTests */, + D2F32FAE2E90B26500B1BC56 /* AscentlyTests */, ); - name = OpenClimbTests; + name = AscentlyTests; packageProductDependencies = ( ); - productName = OpenClimbTests; - productReference = D2F32FAD2E90B26500B1BC56 /* OpenClimbTests.xctest */; + productName = AscentlyTests; + productReference = D2F32FAD2E90B26500B1BC56 /* AscentlyTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; D2FE948A2E78FEE0008CDB25 /* SessionStatusLiveExtension */ = { @@ -251,7 +251,7 @@ }; }; }; - buildConfigurationList = D24C19632E75002A0045894C /* Build configuration list for PBXProject "OpenClimb" */; + buildConfigurationList = D24C19632E75002A0045894C /* Build configuration list for PBXProject "Ascently" */; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -265,9 +265,9 @@ projectDirPath = ""; projectRoot = ""; targets = ( - D24C19672E75002A0045894C /* OpenClimb */, + D24C19672E75002A0045894C /* Ascently */, D2FE948A2E78FEE0008CDB25 /* SessionStatusLiveExtension */, - D2F32FAC2E90B26500B1BC56 /* OpenClimbTests */, + D2F32FAC2E90B26500B1BC56 /* AscentlyTests */, ); }; /* End PBXProject section */ @@ -323,7 +323,7 @@ /* Begin PBXTargetDependency section */ D2F32FB22E90B26500B1BC56 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D24C19672E75002A0045894C /* OpenClimb */; + target = D24C19672E75002A0045894C /* Ascently */; targetProxy = D2F32FB12E90B26500B1BC56 /* PBXContainerItemProxy */; }; D2FE949F2E78FEE1008CDB25 /* PBXTargetDependency */ = { @@ -462,7 +462,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = OpenClimb/OpenClimb.entitlements; + CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 25; @@ -470,12 +470,12 @@ DRIVERKIT_DEPLOYMENT_TARGET = 24.6; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = OpenClimb/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = OpenClimb; + INFOPLIST_FILE = Ascently/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Ascently; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; - INFOPLIST_KEY_NSCameraUsageDescription = "OpenClimb needs camera access to take photos of climbing problems."; - INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "OpenClimb needs access to your photo library to save and display climbing problem images."; + INFOPLIST_KEY_NSCameraUsageDescription = "Ascently needs camera access to take photos of climbing problems."; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Ascently needs access to your photo library to save and display climbing problem images."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -487,8 +487,8 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 15.6; - MARKETING_VERSION = 1.4.0; - PRODUCT_BUNDLE_IDENTIFIER = com.atridad.OpenClimb; + MARKETING_VERSION = 2.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -510,7 +510,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = OpenClimb/OpenClimb.entitlements; + CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 25; @@ -518,12 +518,12 @@ DRIVERKIT_DEPLOYMENT_TARGET = 24.6; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = OpenClimb/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = OpenClimb; + INFOPLIST_FILE = Ascently/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Ascently; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; - INFOPLIST_KEY_NSCameraUsageDescription = "OpenClimb needs camera access to take photos of climbing problems."; - INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "OpenClimb needs access to your photo library to save and display climbing problem images."; + INFOPLIST_KEY_NSCameraUsageDescription = "Ascently needs camera access to take photos of climbing problems."; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Ascently needs access to your photo library to save and display climbing problem images."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -535,8 +535,8 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 15.6; - MARKETING_VERSION = 1.4.0; - PRODUCT_BUNDLE_IDENTIFIER = com.atridad.OpenClimb; + MARKETING_VERSION = 2.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -562,7 +562,7 @@ DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.OpenClimb.Watch.OpenClimbTests; + PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.OpenClimb.Watch.AscentlyTests; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; @@ -570,7 +570,7 @@ SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OpenClimb.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/OpenClimb"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Ascently.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/OpenClimb"; }; name = Debug; }; @@ -583,7 +583,7 @@ DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.OpenClimb.Watch.OpenClimbTests; + PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.OpenClimb.Watch.AscentlyTests; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; @@ -591,7 +591,7 @@ SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OpenClimb.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/OpenClimb"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Ascently.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/OpenClimb"; }; name = Release; }; @@ -602,7 +602,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = SessionStatusLive/Info.plist; @@ -613,8 +613,8 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.4.0; - PRODUCT_BUNDLE_IDENTIFIER = com.atridad.OpenClimb.SessionStatusLive; + MARKETING_VERSION = 2.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.SessionStatusLive; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -632,7 +632,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = SessionStatusLive/Info.plist; @@ -643,8 +643,8 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.4.0; - PRODUCT_BUNDLE_IDENTIFIER = com.atridad.OpenClimb.SessionStatusLive; + MARKETING_VERSION = 2.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.SessionStatusLive; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -658,7 +658,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - D24C19632E75002A0045894C /* Build configuration list for PBXProject "OpenClimb" */ = { + D24C19632E75002A0045894C /* Build configuration list for PBXProject "Ascently" */ = { isa = XCConfigurationList; buildConfigurations = ( D24C19712E75002A0045894C /* Debug */, @@ -667,7 +667,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D24C19732E75002A0045894C /* Build configuration list for PBXNativeTarget "OpenClimb" */ = { + D24C19732E75002A0045894C /* Build configuration list for PBXNativeTarget "Ascently" */ = { isa = XCConfigurationList; buildConfigurations = ( D24C19742E75002A0045894C /* Debug */, @@ -676,7 +676,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D2F32FB52E90B26500B1BC56 /* Build configuration list for PBXNativeTarget "OpenClimbTests" */ = { + D2F32FB52E90B26500B1BC56 /* Build configuration list for PBXNativeTarget "AscentlyTests" */ = { isa = XCConfigurationList; buildConfigurations = ( D2F32FB32E90B26500B1BC56 /* Debug */, diff --git a/ios/OpenClimb.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Ascently.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from ios/OpenClimb.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to ios/Ascently.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/ios/OpenClimb.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Ascently.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from ios/OpenClimb.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to ios/Ascently.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/IDEFindNavigatorScopes.plist b/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/IDEFindNavigatorScopes.plist similarity index 100% rename from ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/IDEFindNavigatorScopes.plist rename to ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/IDEFindNavigatorScopes.plist diff --git a/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate b/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..a9d59782813270d58ffd9d3165ba4df3e4d7fefc GIT binary patch literal 227444 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 literal 0 HcmV?d00001 diff --git a/ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/WorkspaceSettings.xcsettings b/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/WorkspaceSettings.xcsettings similarity index 100% rename from ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/WorkspaceSettings.xcsettings rename to ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/WorkspaceSettings.xcsettings diff --git a/ios/OpenClimb.xcodeproj/xcshareddata/xcschemes/OpenClimb.xcscheme b/ios/Ascently.xcodeproj/xcshareddata/xcschemes/OpenClimb.xcscheme similarity index 80% rename from ios/OpenClimb.xcodeproj/xcshareddata/xcschemes/OpenClimb.xcscheme rename to ios/Ascently.xcodeproj/xcshareddata/xcschemes/OpenClimb.xcscheme index e330960..7c887db 100644 --- a/ios/OpenClimb.xcodeproj/xcshareddata/xcschemes/OpenClimb.xcscheme +++ b/ios/Ascently.xcodeproj/xcshareddata/xcschemes/OpenClimb.xcscheme @@ -15,9 +15,9 @@ + BuildableName = "Ascently.app" + BlueprintName = "Ascently" + ReferencedContainer = "container:Ascently.xcodeproj"> @@ -34,9 +34,9 @@ + BuildableName = "AscentlyTests.xctest" + BlueprintName = "AscentlyTests" + ReferencedContainer = "container:Ascently.xcodeproj"> @@ -56,9 +56,9 @@ + BuildableName = "Ascently.app" + BlueprintName = "Ascently" + ReferencedContainer = "container:Ascently.xcodeproj"> @@ -73,9 +73,9 @@ + BuildableName = "Ascently.app" + BlueprintName = "Ascently" + ReferencedContainer = "container:Ascently.xcodeproj"> diff --git a/ios/OpenClimb.xcodeproj/xcshareddata/xcschemes/SessionStatusLiveExtension.xcscheme b/ios/Ascently.xcodeproj/xcshareddata/xcschemes/SessionStatusLiveExtension.xcscheme similarity index 85% rename from ios/OpenClimb.xcodeproj/xcshareddata/xcschemes/SessionStatusLiveExtension.xcscheme rename to ios/Ascently.xcodeproj/xcshareddata/xcschemes/SessionStatusLiveExtension.xcscheme index b074f3a..5069e35 100644 --- a/ios/OpenClimb.xcodeproj/xcshareddata/xcschemes/SessionStatusLiveExtension.xcscheme +++ b/ios/Ascently.xcodeproj/xcshareddata/xcschemes/SessionStatusLiveExtension.xcscheme @@ -19,7 +19,7 @@ BlueprintIdentifier = "D2FE948A2E78FEE0008CDB25" BuildableName = "SessionStatusLiveExtension.appex" BlueprintName = "SessionStatusLiveExtension" - ReferencedContainer = "container:OpenClimb.xcodeproj"> + ReferencedContainer = "container:Ascently.xcodeproj"> + BuildableName = "Ascently.app" + BlueprintName = "Ascently" + ReferencedContainer = "container:Ascently.xcodeproj"> @@ -51,9 +51,9 @@ + BuildableName = "AscentlyTests.xctest" + BlueprintName = "AscentlyTests" + ReferencedContainer = "container:Ascently.xcodeproj"> @@ -75,9 +75,9 @@ + BuildableName = "Ascently.app" + BlueprintName = "Ascently" + ReferencedContainer = "container:Ascently.xcodeproj"> @@ -111,9 +111,9 @@ + BuildableName = "Ascently.app" + BlueprintName = "Ascently" + ReferencedContainer = "container:Ascently.xcodeproj"> diff --git a/ios/OpenClimb.xcodeproj/xcuserdata/atridad.xcuserdatad/xcschemes/xcschememanagement.plist b/ios/Ascently.xcodeproj/xcuserdata/atridad.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 80% rename from ios/OpenClimb.xcodeproj/xcuserdata/atridad.xcuserdatad/xcschemes/xcschememanagement.plist rename to ios/Ascently.xcodeproj/xcuserdata/atridad.xcuserdatad/xcschemes/xcschememanagement.plist index 7dfcbe0..dfb4a31 100644 --- a/ios/OpenClimb.xcodeproj/xcuserdata/atridad.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/ios/Ascently.xcodeproj/xcuserdata/atridad.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,7 +4,12 @@ SchemeUserState - OpenClimb.xcscheme_^#shared#^_ + AscentlyTests.xcscheme_^#shared#^_ + + orderHint + 2 + + Ascently.xcscheme_^#shared#^_ orderHint 1 diff --git a/ios/OpenClimb/OpenClimb.entitlements b/ios/Ascently/Ascently.entitlements similarity index 88% rename from ios/OpenClimb/OpenClimb.entitlements rename to ios/Ascently/Ascently.entitlements index 7810965..b00abdd 100644 --- a/ios/OpenClimb/OpenClimb.entitlements +++ b/ios/Ascently/Ascently.entitlements @@ -4,7 +4,7 @@ com.apple.security.application-groups - group.com.atridad.OpenClimb + group.com.atridad.Ascently com.apple.developer.healthkit diff --git a/ios/OpenClimb/OpenClimbApp.swift b/ios/Ascently/AscentlyApp.swift similarity index 80% rename from ios/OpenClimb/OpenClimbApp.swift rename to ios/Ascently/AscentlyApp.swift index b1cab24..1d10d71 100644 --- a/ios/OpenClimb/OpenClimbApp.swift +++ b/ios/Ascently/AscentlyApp.swift @@ -1,8 +1,7 @@ - import SwiftUI @main -struct OpenClimbApp: App { +struct AscentlyApp: App { var body: some Scene { WindowGroup { ContentView() diff --git a/ios/OpenClimb/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/Ascently/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AccentColor.colorset/Contents.json rename to ios/Ascently/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Ascently/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/Contents.json rename to ios/Ascently/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png rename to ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png diff --git a/ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_1024_dark.png b/ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_1024_dark.png similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_1024_dark.png rename to ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_1024_dark.png diff --git a/ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_1024_tinted.png b/ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_1024_tinted.png similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_1024_tinted.png rename to ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_1024_tinted.png diff --git a/ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_dark_template.svg b/ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_dark_template.svg similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_dark_template.svg rename to ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_dark_template.svg diff --git a/ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_light_template.svg b/ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_light_template.svg similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_light_template.svg rename to ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_light_template.svg diff --git a/ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_tinted_template.svg b/ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_tinted_template.svg similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppIcon.appiconset/app_icon_tinted_template.svg rename to ios/Ascently/Assets.xcassets/AppIcon.appiconset/app_icon_tinted_template.svg diff --git a/ios/OpenClimb/Assets.xcassets/AppLogo.imageset/Contents.json b/ios/Ascently/Assets.xcassets/AppLogo.imageset/Contents.json similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppLogo.imageset/Contents.json rename to ios/Ascently/Assets.xcassets/AppLogo.imageset/Contents.json diff --git a/ios/OpenClimb/Assets.xcassets/AppLogo.imageset/app_logo_256.png b/ios/Ascently/Assets.xcassets/AppLogo.imageset/app_logo_256.png similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppLogo.imageset/app_logo_256.png rename to ios/Ascently/Assets.xcassets/AppLogo.imageset/app_logo_256.png diff --git a/ios/OpenClimb/Assets.xcassets/AppLogo.imageset/app_logo_256_dark.png b/ios/Ascently/Assets.xcassets/AppLogo.imageset/app_logo_256_dark.png similarity index 100% rename from ios/OpenClimb/Assets.xcassets/AppLogo.imageset/app_logo_256_dark.png rename to ios/Ascently/Assets.xcassets/AppLogo.imageset/app_logo_256_dark.png diff --git a/ios/OpenClimb/Assets.xcassets/Contents.json b/ios/Ascently/Assets.xcassets/Contents.json similarity index 100% rename from ios/OpenClimb/Assets.xcassets/Contents.json rename to ios/Ascently/Assets.xcassets/Contents.json diff --git a/ios/OpenClimb/Components/AsyncImageView.swift b/ios/Ascently/Components/AsyncImageView.swift similarity index 100% rename from ios/OpenClimb/Components/AsyncImageView.swift rename to ios/Ascently/Components/AsyncImageView.swift diff --git a/ios/OpenClimb/Components/CameraImagePicker.swift b/ios/Ascently/Components/CameraImagePicker.swift similarity index 100% rename from ios/OpenClimb/Components/CameraImagePicker.swift rename to ios/Ascently/Components/CameraImagePicker.swift diff --git a/ios/OpenClimb/Components/PhotoOptionSheet.swift b/ios/Ascently/Components/PhotoOptionSheet.swift similarity index 100% rename from ios/OpenClimb/Components/PhotoOptionSheet.swift rename to ios/Ascently/Components/PhotoOptionSheet.swift diff --git a/ios/OpenClimb/ContentView.swift b/ios/Ascently/ContentView.swift similarity index 100% rename from ios/OpenClimb/ContentView.swift rename to ios/Ascently/ContentView.swift diff --git a/ios/OpenClimb/Info.plist b/ios/Ascently/Info.plist similarity index 100% rename from ios/OpenClimb/Info.plist rename to ios/Ascently/Info.plist diff --git a/ios/OpenClimb/Models/ActivityAttributes.swift b/ios/Ascently/Models/ActivityAttributes.swift similarity index 100% rename from ios/OpenClimb/Models/ActivityAttributes.swift rename to ios/Ascently/Models/ActivityAttributes.swift diff --git a/ios/OpenClimb/Models/BackupFormat.swift b/ios/Ascently/Models/BackupFormat.swift similarity index 99% rename from ios/OpenClimb/Models/BackupFormat.swift rename to ios/Ascently/Models/BackupFormat.swift index 4691651..9665661 100644 --- a/ios/OpenClimb/Models/BackupFormat.swift +++ b/ios/Ascently/Models/BackupFormat.swift @@ -5,7 +5,7 @@ import Foundation // MARK: - Backup Format Specification v2.0 -/// Root structure for OpenClimb backup data +/// Root structure for Ascently backup data struct DeletedItem: Codable, Hashable { let id: String let type: String // "gym", "problem", "session", "attempt" diff --git a/ios/OpenClimb/Models/DataModels.swift b/ios/Ascently/Models/DataModels.swift similarity index 100% rename from ios/OpenClimb/Models/DataModels.swift rename to ios/Ascently/Models/DataModels.swift diff --git a/ios/OpenClimb/Services/HealthKitService.swift b/ios/Ascently/Services/HealthKitService.swift similarity index 100% rename from ios/OpenClimb/Services/HealthKitService.swift rename to ios/Ascently/Services/HealthKitService.swift diff --git a/ios/OpenClimb/Services/SyncService.swift b/ios/Ascently/Services/SyncService.swift similarity index 99% rename from ios/OpenClimb/Services/SyncService.swift rename to ios/Ascently/Services/SyncService.swift index 3460f46..72b56bd 100644 --- a/ios/OpenClimb/Services/SyncService.swift +++ b/ios/Ascently/Services/SyncService.swift @@ -501,7 +501,7 @@ class SyncService: ObservableObject { // Update local deletions with merged list dataManager.clearDeletedItems() if let data = try? JSONEncoder().encode(uniqueDeletions) { - UserDefaults.standard.set(data, forKey: "openclimb_deleted_items") + UserDefaults.standard.set(data, forKey: "ascently_deleted_items") } // Upload merged data back to server @@ -648,7 +648,7 @@ class SyncService: ObservableObject { // Import deletion records to prevent future resurrections dataManager.clearDeletedItems() if let data = try? JSONEncoder().encode(backup.deletedItems) { - UserDefaults.standard.set(data, forKey: "openclimb_deleted_items") + UserDefaults.standard.set(data, forKey: "ascently_deleted_items") print("iOS IMPORT: Imported \(backup.deletedItems.count) deletion records") } diff --git a/ios/OpenClimb/Utils/AppIconHelper.swift b/ios/Ascently/Utils/AppIconHelper.swift similarity index 100% rename from ios/OpenClimb/Utils/AppIconHelper.swift rename to ios/Ascently/Utils/AppIconHelper.swift diff --git a/ios/OpenClimb/Utils/DataStateManager.swift b/ios/Ascently/Utils/DataStateManager.swift similarity index 95% rename from ios/OpenClimb/Utils/DataStateManager.swift rename to ios/Ascently/Utils/DataStateManager.swift index e7ff5ea..69c05b4 100644 --- a/ios/OpenClimb/Utils/DataStateManager.swift +++ b/ios/Ascently/Utils/DataStateManager.swift @@ -9,8 +9,8 @@ class DataStateManager { private let userDefaults = UserDefaults.standard private enum Keys { - static let lastModified = "openclimb_data_last_modified" - static let initialized = "openclimb_data_state_initialized" + static let lastModified = "ascently_data_last_modified" + static let initialized = "ascently_data_state_initialized" } static let shared = DataStateManager() diff --git a/ios/OpenClimb/Utils/IconTestView.swift b/ios/Ascently/Utils/IconTestView.swift similarity index 100% rename from ios/OpenClimb/Utils/IconTestView.swift rename to ios/Ascently/Utils/IconTestView.swift diff --git a/ios/OpenClimb/Utils/ImageManager.swift b/ios/Ascently/Utils/ImageManager.swift similarity index 93% rename from ios/OpenClimb/Utils/ImageManager.swift rename to ios/Ascently/Utils/ImageManager.swift index 80bdcbb..2edae1a 100644 --- a/ios/OpenClimb/Utils/ImageManager.swift +++ b/ios/Ascently/Utils/ImageManager.swift @@ -8,13 +8,17 @@ class ImageManager { private let thumbnailCache = NSCache() private let fileManager = FileManager.default - private let appSupportDirectoryName = "OpenClimb" + private let appSupportDirectoryName = "Ascently" private let imagesDirectoryName = "Images" private let backupDirectoryName = "ImageBackups" private let migrationStateFile = "migration_state.json" private let migrationLockFile = "migration.lock" + // Legacy directory name for migration + private let legacyAppSupportDirectoryName = "OpenClimb" + private init() { + migrateFromOpenClimbDirectoryIfNeeded() createDirectoriesIfNeeded() // Debug-safe initialization with extra checks @@ -58,6 +62,52 @@ class ImageManager { legacyDocumentsDirectory.appendingPathComponent("OpenClimbImages") } + /// Legacy OpenClimb app support directory for migration + private var legacyOpenClimbAppSupportDirectory: URL? { + guard + let appSupportBase = fileManager.urls( + for: .applicationSupportDirectory, in: .userDomainMask + ).first + else { + return nil + } + return appSupportBase.appendingPathComponent(legacyAppSupportDirectoryName) + } + + /// Migrate images from OpenClimb directory to Ascently directory + private func migrateFromOpenClimbDirectoryIfNeeded() { + guard let legacyDir = legacyOpenClimbAppSupportDirectory, + fileManager.fileExists(atPath: legacyDir.path), + !fileManager.fileExists(atPath: appSupportDirectory.path) + else { + return + } + + print("🔄 Migrating images from OpenClimb to Ascently directory...") + + do { + // Create parent directory if needed + try fileManager.createDirectory( + at: appSupportDirectory.deletingLastPathComponent(), + withIntermediateDirectories: true, + attributes: nil) + + // Move the entire directory + try fileManager.moveItem(at: legacyDir, to: appSupportDirectory) + print("Successfully migrated image directory from OpenClimb to Ascently") + } catch { + print("❌ Failed to migrate image directory: \(error)") + // If move fails, try to copy instead + do { + try fileManager.copyItem(at: legacyDir, to: appSupportDirectory) + print("Successfully copied image directory from OpenClimb to Ascently") + // Don't remove the old directory in case of issues + } catch { + print("❌ Failed to copy image directory: \(error)") + } + } + } + var legacyImportImagesDirectory: URL { legacyDocumentsDirectory.appendingPathComponent("images") } @@ -671,7 +721,7 @@ class ImageManager { let previousDir = findPreviousAppSupportImages() print( """ - OpenClimb Image Storage: + Ascently Image Storage: - App Support: \(appSupportDirectory.path) - Images: \(imagesDirectory.path) (\(info.primaryCount) files) - Backups: \(backupDirectory.path) (\(info.backupCount) files) @@ -842,8 +892,9 @@ class ImageManager { continue } - // Check if it's an OpenClimb directory but not the current one - if url.lastPathComponent.contains("OpenClimb") + // Check if it's an OpenClimb or Ascently directory but not the current one + if (url.lastPathComponent.contains("OpenClimb") + || url.lastPathComponent.contains("Ascently")) && url.path != appSupportDirectory.path { let imagesDir = url.appendingPathComponent(imagesDirectoryName) diff --git a/ios/OpenClimb/Utils/ImageNamingUtils.swift b/ios/Ascently/Utils/ImageNamingUtils.swift similarity index 100% rename from ios/OpenClimb/Utils/ImageNamingUtils.swift rename to ios/Ascently/Utils/ImageNamingUtils.swift diff --git a/ios/OpenClimb/Utils/OrientationAwareImage.swift b/ios/Ascently/Utils/OrientationAwareImage.swift similarity index 100% rename from ios/OpenClimb/Utils/OrientationAwareImage.swift rename to ios/Ascently/Utils/OrientationAwareImage.swift diff --git a/ios/OpenClimb/Utils/ZipUtils.swift b/ios/Ascently/Utils/ZipUtils.swift similarity index 99% rename from ios/OpenClimb/Utils/ZipUtils.swift rename to ios/Ascently/Utils/ZipUtils.swift index 64d4fbd..c64647f 100644 --- a/ios/OpenClimb/Utils/ZipUtils.swift +++ b/ios/Ascently/Utils/ZipUtils.swift @@ -199,8 +199,8 @@ struct ZipUtils { referencedImagePaths: Set ) -> String { return """ - OpenClimb Export Metadata - ======================= + Ascently Export Metadata + ======================== Export Date: \(exportData.exportedAt) Gyms: \(exportData.gyms.count) Problems: \(exportData.problems.count) diff --git a/ios/OpenClimb/ViewModels/ClimbingDataManager.swift b/ios/Ascently/ViewModels/ClimbingDataManager.swift similarity index 93% rename from ios/OpenClimb/ViewModels/ClimbingDataManager.swift rename to ios/Ascently/ViewModels/ClimbingDataManager.swift index 79941cf..81d9a9e 100644 --- a/ios/OpenClimb/ViewModels/ClimbingDataManager.swift +++ b/ios/Ascently/ViewModels/ClimbingDataManager.swift @@ -25,9 +25,12 @@ class ClimbingDataManager: ObservableObject { @Published var successMessage: String? private let userDefaults = UserDefaults.standard - private let sharedUserDefaults = UserDefaults(suiteName: "group.com.atridad.OpenClimb") + private let sharedUserDefaults = UserDefaults(suiteName: "group.com.atridad.Ascently") private let encoder = JSONEncoder() private let decoder = JSONDecoder() + + // Flag to track if migration has been performed + private let migrationKey = "ascently_data_migrated_from_openclimb" nonisolated(unsafe) private var liveActivityObserver: NSObjectProtocol? nonisolated(unsafe) private var migrationObserver: NSObjectProtocol? @@ -37,6 +40,16 @@ class ClimbingDataManager: ObservableObject { @Published var isSyncing = false private enum Keys { + static let gyms = "ascently_gyms" + static let problems = "ascently_problems" + static let sessions = "ascently_sessions" + static let attempts = "ascently_attempts" + static let activeSession = "ascently_active_session" + static let deletedItems = "ascently_deleted_items" + } + + // Legacy keys for migration + private enum LegacyKeys { static let gyms = "openclimb_gyms" static let problems = "openclimb_problems" static let sessions = "openclimb_sessions" @@ -68,6 +81,7 @@ class ClimbingDataManager: ObservableObject { init() { _ = ImageManager.shared + migrateFromOpenClimbIfNeeded() loadAllData() setupLiveActivityNotifications() setupMigrationNotifications() @@ -94,6 +108,74 @@ class ClimbingDataManager: ObservableObject { } } + /// Migrate data from OpenClimb keys to Ascently keys + private func migrateFromOpenClimbIfNeeded() { + // Check if migration has already been performed + if userDefaults.bool(forKey: migrationKey) { + return + } + + print("Starting migration from OpenClimb to Ascently keys...") + var migrationCount = 0 + + // Migrate each data type if it exists in old format but not in new format + let migrations = [ + (LegacyKeys.gyms, Keys.gyms), + (LegacyKeys.problems, Keys.problems), + (LegacyKeys.sessions, Keys.sessions), + (LegacyKeys.attempts, Keys.attempts), + (LegacyKeys.activeSession, Keys.activeSession), + (LegacyKeys.deletedItems, Keys.deletedItems), + ] + + for (oldKey, newKey) in migrations { + if let oldData = userDefaults.data(forKey: oldKey), + userDefaults.data(forKey: newKey) == nil + { + userDefaults.set(oldData, forKey: newKey) + userDefaults.removeObject(forKey: oldKey) + migrationCount += 1 + print("✅ Migrated: \(oldKey) → \(newKey)") + } + } + + // Also migrate shared UserDefaults for widgets + if let sharedDefaults = sharedUserDefaults { + for (oldKey, newKey) in migrations { + if let oldData = sharedDefaults.data(forKey: oldKey), + sharedDefaults.data(forKey: newKey) == nil + { + sharedDefaults.set(oldData, forKey: newKey) + sharedDefaults.removeObject(forKey: oldKey) + print("✅ Migrated shared: \(oldKey) → \(newKey)") + } + } + } + + // Migrate DataStateManager keys + let legacyDataStateKey = "openclimb_data_last_modified" + let newDataStateKey = "ascently_data_last_modified" + if let lastModified = userDefaults.string(forKey: legacyDataStateKey), + userDefaults.string(forKey: newDataStateKey) == nil + { + userDefaults.set(lastModified, forKey: newDataStateKey) + userDefaults.removeObject(forKey: legacyDataStateKey) + migrationCount += 1 + print("✅ Migrated data state timestamp") + } + + // Mark migration as completed + userDefaults.set(true, forKey: migrationKey) + + if migrationCount > 0 { + print( + "Migration completed! Migrated \(migrationCount) data items from OpenClimb to Ascently" + ) + } else { + print("No OpenClimb data found to migrate") + } + } + private func loadAllData() { loadGyms() loadProblems() diff --git a/ios/OpenClimb/ViewModels/LiveActivityManager.swift b/ios/Ascently/ViewModels/LiveActivityManager.swift similarity index 100% rename from ios/OpenClimb/ViewModels/LiveActivityManager.swift rename to ios/Ascently/ViewModels/LiveActivityManager.swift diff --git a/ios/OpenClimb/Views/AddEdit/AddAttemptView.swift b/ios/Ascently/Views/AddEdit/AddAttemptView.swift similarity index 100% rename from ios/OpenClimb/Views/AddEdit/AddAttemptView.swift rename to ios/Ascently/Views/AddEdit/AddAttemptView.swift diff --git a/ios/OpenClimb/Views/AddEdit/AddEditGymView.swift b/ios/Ascently/Views/AddEdit/AddEditGymView.swift similarity index 100% rename from ios/OpenClimb/Views/AddEdit/AddEditGymView.swift rename to ios/Ascently/Views/AddEdit/AddEditGymView.swift diff --git a/ios/OpenClimb/Views/AddEdit/AddEditProblemView.swift b/ios/Ascently/Views/AddEdit/AddEditProblemView.swift similarity index 100% rename from ios/OpenClimb/Views/AddEdit/AddEditProblemView.swift rename to ios/Ascently/Views/AddEdit/AddEditProblemView.swift diff --git a/ios/OpenClimb/Views/AddEdit/AddEditSessionView.swift b/ios/Ascently/Views/AddEdit/AddEditSessionView.swift similarity index 100% rename from ios/OpenClimb/Views/AddEdit/AddEditSessionView.swift rename to ios/Ascently/Views/AddEdit/AddEditSessionView.swift diff --git a/ios/OpenClimb/Views/AnalyticsView.swift b/ios/Ascently/Views/AnalyticsView.swift similarity index 100% rename from ios/OpenClimb/Views/AnalyticsView.swift rename to ios/Ascently/Views/AnalyticsView.swift diff --git a/ios/OpenClimb/Views/Detail/GymDetailView.swift b/ios/Ascently/Views/Detail/GymDetailView.swift similarity index 100% rename from ios/OpenClimb/Views/Detail/GymDetailView.swift rename to ios/Ascently/Views/Detail/GymDetailView.swift diff --git a/ios/OpenClimb/Views/Detail/ProblemDetailView.swift b/ios/Ascently/Views/Detail/ProblemDetailView.swift similarity index 100% rename from ios/OpenClimb/Views/Detail/ProblemDetailView.swift rename to ios/Ascently/Views/Detail/ProblemDetailView.swift diff --git a/ios/OpenClimb/Views/Detail/SessionDetailView.swift b/ios/Ascently/Views/Detail/SessionDetailView.swift similarity index 100% rename from ios/OpenClimb/Views/Detail/SessionDetailView.swift rename to ios/Ascently/Views/Detail/SessionDetailView.swift diff --git a/ios/OpenClimb/Views/GymsView.swift b/ios/Ascently/Views/GymsView.swift similarity index 100% rename from ios/OpenClimb/Views/GymsView.swift rename to ios/Ascently/Views/GymsView.swift diff --git a/ios/OpenClimb/Views/LiveActivityDebugView.swift b/ios/Ascently/Views/LiveActivityDebugView.swift similarity index 100% rename from ios/OpenClimb/Views/LiveActivityDebugView.swift rename to ios/Ascently/Views/LiveActivityDebugView.swift diff --git a/ios/OpenClimb/Views/ProblemsView.swift b/ios/Ascently/Views/ProblemsView.swift similarity index 100% rename from ios/OpenClimb/Views/ProblemsView.swift rename to ios/Ascently/Views/ProblemsView.swift diff --git a/ios/OpenClimb/Views/SessionsView.swift b/ios/Ascently/Views/SessionsView.swift similarity index 100% rename from ios/OpenClimb/Views/SessionsView.swift rename to ios/Ascently/Views/SessionsView.swift diff --git a/ios/OpenClimb/Views/SettingsView.swift b/ios/Ascently/Views/SettingsView.swift similarity index 99% rename from ios/OpenClimb/Views/SettingsView.swift rename to ios/Ascently/Views/SettingsView.swift index e11efb9..5cbdd3a 100644 --- a/ios/OpenClimb/Views/SettingsView.swift +++ b/ios/Ascently/Views/SettingsView.swift @@ -263,7 +263,7 @@ struct AppInfoSection: View { .resizable() .frame(width: 24, height: 24) VStack(alignment: .leading) { - Text("OpenClimb") + Text("Ascently") .font(.headline) Text("Track your climbing progress") .font(.caption) @@ -332,7 +332,7 @@ struct ExportDataView: View { ShareLink( item: fileURL, preview: SharePreview( - "OpenClimb Data Export", + "Ascently Data Export", image: Image("AppLogo")) ) { Label("Share Data", systemImage: "square.and.arrow.up") @@ -385,7 +385,7 @@ struct ExportDataView: View { let isoString = formatter.string(from: Date()) let timestamp = isoString.replacingOccurrences(of: ":", with: "-") .replacingOccurrences(of: ".", with: "-") - let filename = "openclimb_export_\(timestamp).zip" + let filename = "ascently_export_\(timestamp).zip" guard let documentsURL = FileManager.default.urls( diff --git a/ios/OpenClimbTests/OpenClimbTests.swift b/ios/AscentlyTests/AscentlyTests.swift similarity index 99% rename from ios/OpenClimbTests/OpenClimbTests.swift rename to ios/AscentlyTests/AscentlyTests.swift index 104c622..71fbe0f 100644 --- a/ios/OpenClimbTests/OpenClimbTests.swift +++ b/ios/AscentlyTests/AscentlyTests.swift @@ -1,6 +1,6 @@ import XCTest -final class OpenClimbTests: XCTestCase { +final class AscentlyTests: XCTestCase { override func setUpWithError() throws { } diff --git a/ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate b/ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 4b2b45dfe8b31c93f653367e0c98aff6f4f7bb61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166689 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 Ihr84N4`W66)&Kwi diff --git a/ios/README.md b/ios/README.md index 53b59c6..67d41bc 100644 --- a/ios/README.md +++ b/ios/README.md @@ -1,10 +1,10 @@ -# OpenClimb for iOS +# Ascently for iOS -The native iOS, watchOS, and widget client for OpenClimb, built with Swift and SwiftUI. +The native iOS, watchOS, and widget client for Ascently, built with Swift and SwiftUI. ## Project Structure -This is a standard Xcode project. The main app code is in the `OpenClimb/` directory. +This is a standard Xcode project. The main app code is in the `Ascently/` directory. - `Models/`: Swift `Codable` models (`Problem`, `Gym`, `ClimbSession`) that match the Android app. - `ViewModels/`: App state and logic. `ClimbingDataManager` is the core here, handling data with SwiftData. @@ -16,8 +16,7 @@ This is a standard Xcode project. The main app code is in the `OpenClimb/` direc ## Other Targets -- `OpenClimbWatch/`: The watchOS app for tracking sessions. - `ClimbingActivityWidget/`: A home screen widget. - `SessionStatusLive/`: A Live Activity for the lock screen. -The app is built to be offline-first. All data is stored locally on your device and works without an internet connection. \ No newline at end of file +The app is built to be offline-first. All data is stored locally on your device and works without an internet connection. diff --git a/ios/SessionStatusLive/SessionStatusLive.swift b/ios/SessionStatusLive/SessionStatusLive.swift index 89bc119..afdad76 100644 --- a/ios/SessionStatusLive/SessionStatusLive.swift +++ b/ios/SessionStatusLive/SessionStatusLive.swift @@ -49,10 +49,10 @@ struct ClimbingStatsProvider: TimelineProvider { } private func loadClimbingStats() -> ClimbingStats { - let userDefaults = UserDefaults(suiteName: "group.com.atridad.OpenClimb") + let userDefaults = UserDefaults(suiteName: "group.com.atridad.Ascently") // Load attempts from UserDefaults - guard let attemptsData = userDefaults?.data(forKey: "openclimb_attempts"), + guard let attemptsData = userDefaults?.data(forKey: "ascently_attempts"), let attempts = try? JSONDecoder().decode([WidgetAttempt].self, from: attemptsData) else { return ClimbingStats( @@ -60,11 +60,11 @@ struct ClimbingStatsProvider: TimelineProvider { } // Load sessions for streak calculation - let sessionsData = (userDefaults?.data(forKey: "openclimb_sessions"))! + let sessionsData = (userDefaults?.data(forKey: "ascently_sessions"))! let sessions = (try? JSONDecoder().decode([WidgetSession].self, from: sessionsData)) ?? [] // Load gyms for favorite gym name - let gymsData = (userDefaults?.data(forKey: "openclimb_gyms"))! + let gymsData = (userDefaults?.data(forKey: "ascently_gyms"))! let gyms = (try? JSONDecoder().decode([WidgetGym].self, from: gymsData)) ?? [] let calendar = Calendar.current diff --git a/ios/SessionStatusLive/SessionStatusLiveControl.swift b/ios/SessionStatusLive/SessionStatusLiveControl.swift index 70f5ae6..3471c8f 100644 --- a/ios/SessionStatusLive/SessionStatusLiveControl.swift +++ b/ios/SessionStatusLive/SessionStatusLiveControl.swift @@ -6,7 +6,7 @@ import SwiftUI import WidgetKit struct SessionStatusLiveControl: ControlWidget { - static let kind: String = "com.atridad.OpenClimb.SessionStatusLive" + static let kind: String = "com.atridad.Ascently.SessionStatusLive" var body: some ControlWidgetConfiguration { AppIntentControlConfiguration( diff --git a/ios/SessionStatusLiveExtension.entitlements b/ios/SessionStatusLiveExtension.entitlements index 2630f0c..f207868 100644 --- a/ios/SessionStatusLiveExtension.entitlements +++ b/ios/SessionStatusLiveExtension.entitlements @@ -4,7 +4,7 @@ com.apple.security.application-groups - group.com.atridad.OpenClimb + group.com.atridad.Ascently diff --git a/sync/.env.example b/sync/.env.example index f39de10..d8dcb2b 100644 --- a/sync/.env.example +++ b/sync/.env.example @@ -1,6 +1,6 @@ # Required AUTH_TOKEN=your-secure-secret-token-here -IMAGE="git.atri.dad/atridad/openclimb-sync:latest" +IMAGE="git.atri.dad/atridad/ascently-sync:latest" APP_PORT=1337 ROOT_DIR="./data" diff --git a/sync/.gitignore b/sync/.gitignore index 9fc8fde..faab951 100644 --- a/sync/.gitignore +++ b/sync/.gitignore @@ -1,6 +1,6 @@ # Binaries sync-server -openclimb-sync +ascently-sync # Go workspace file go.work diff --git a/sync/README.md b/sync/README.md index 53e3a33..66e1f39 100644 --- a/sync/README.md +++ b/sync/README.md @@ -1,21 +1,21 @@ # Sync Server -A simple Go server for self-hosting your OpenClimb sync data. +A simple Go server for self-hosting your Ascently sync data. ## How It Works -This server is dead simple. It uses a single `openclimb.json` file for your data and a directory for images. The last client to upload wins, overwriting the old data. Authentication is just a static bearer token. +This server is dead simple. It uses a single `ascently.json` file for your data and a directory for images. The last client to upload wins, overwriting the old data. Authentication is just a static bearer token. ## Getting Started 1. Create a `.env` file in this directory: ``` - IMAGE=git.atri.dad/atridad/openclimb-sync:latest + IMAGE=git.atri.dad/atridad/ascently-sync:latest APP_PORT=8080 AUTH_TOKEN=your-super-secret-token - DATA_FILE=/data/openclimb.json + DATA_FILE=/data/ascently.json IMAGES_DIR=/data/images - ROOT_DIR=./openclimb-data + ROOT_DIR=./ascently-data ``` Set `AUTH_TOKEN` to a long, random string. `ROOT_DIR` is where the server will store its data on your machine. @@ -29,8 +29,8 @@ This server is dead simple. It uses a single `openclimb.json` file for your data The API is minimal, just enough for the app to work. All endpoints require an `Authorization: Bearer ` header. -- `GET /sync`: Download `openclimb.json`. -- `POST /sync`: Upload `openclimb.json`. +- `GET /sync`: Download `ascently.json`. +- `POST /sync`: Upload `ascently.json`. - `GET /images/{imageName}`: Download an image. - `POST /images/{imageName}`: Upload an image. diff --git a/sync/docker-compose.yml b/sync/docker-compose.yml index 951b61a..63a1270 100644 --- a/sync/docker-compose.yml +++ b/sync/docker-compose.yml @@ -1,5 +1,5 @@ services: - openclimb-sync: + ascently-sync: image: ${IMAGE} ports: - ${APP_PORT}:8080 diff --git a/sync/go.mod b/sync/go.mod index 3103696..3bb9e3e 100644 --- a/sync/go.mod +++ b/sync/go.mod @@ -1,3 +1,3 @@ -module openclimb-sync +module ascently-sync go 1.25 diff --git a/sync/main.go b/sync/main.go index 9d46eb0..040078a 100644 --- a/sync/main.go +++ b/sync/main.go @@ -358,7 +358,7 @@ func main() { http.HandleFunc("/images/upload", server.handleImageUpload) http.HandleFunc("/images/download", server.handleImageDownload) - fmt.Printf("OpenClimb sync server v%s starting on port %s\n", VERSION, port) + fmt.Printf("Ascently sync server v%s starting on port %s\n", VERSION, port) fmt.Printf("Data file: %s\n", dataFile) fmt.Printf("Images directory: %s\n", imagesDir) fmt.Printf("Health check available at /health\n")