diff --git a/app.config.json b/app.config.json index 385d56bf..f746335f 100644 --- a/app.config.json +++ b/app.config.json @@ -25,7 +25,8 @@ "CFBundleAllowMixedLocalizations": true, "CFBundleLocalizations": ["en", "de"], "CFBundleDevelopmentRegion": "en", - "UIViewControllerBasedStatusBarAppearance": true + "UIViewControllerBasedStatusBarAppearance": true, + "NSLocationWhenInUseUsageDescription": "Allow Neuland Next to access your location to show your location on the map." }, "splash": { "image": "./src/assets/splash/splashLight.png", @@ -38,6 +39,10 @@ }, "icon": "./src/assets/appIcons/default.png" }, + "locales": { + "en-US": "./src/localization/en/ios.json", + "de-DE": "./src/localization/de/ios.json" + }, "androidStatusBar": { "translucent": true }, diff --git a/bun.lockb b/bun.lockb index 79c9d077..90f1e447 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/ios/NeulandNext.xcodeproj/project.pbxproj b/ios/NeulandNext.xcodeproj/project.pbxproj index 9eeb3229..6c31308a 100644 --- a/ios/NeulandNext.xcodeproj/project.pbxproj +++ b/ios/NeulandNext.xcodeproj/project.pbxproj @@ -11,12 +11,14 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; + 5EA579CB98A042F5873E8D2B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 15D289C40A50492AA788F5C8 /* InfoPlist.strings */; }; 79A805BC94BC0AD1674AEE08 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 2DE9A45D19FFF21A2FC7974A /* PrivacyInfo.xcprivacy */; }; + 8F67CB1D1CD0F80933516397 /* libPods-NeulandNext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF16343C6EDDD0DDB9165116 /* libPods-NeulandNext.a */; }; B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; }; - B66703B58F15AA5631B440D6 /* libPods-NeulandNext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 39C56A0E8D7A8B5EABAECBB9 /* libPods-NeulandNext.a */; }; B7E3797A2C54A52E006A4193 /* MapLibre in Frameworks */ = {isa = PBXBuildFile; productRef = EC742494CB1CE25F860AF0D3 /* MapLibre */; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; CCE77C0024F64283949166B7 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4A762513EF4F1B924A16CC /* noop-file.swift */; }; + D419EF2B714A439388DCB898 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = AF143DA9DAE546BAA252E633 /* InfoPlist.strings */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -26,15 +28,17 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = NeulandNext/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = NeulandNext/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = NeulandNext/main.m; sourceTree = ""; }; + 15D289C40A50492AA788F5C8 /* InfoPlist.strings */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = "de-DE.lproj/InfoPlist.strings"; sourceTree = ""; }; 2D4A762513EF4F1B924A16CC /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "NeulandNext/noop-file.swift"; sourceTree = ""; }; 2DE9A45D19FFF21A2FC7974A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = NeulandNext/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 39C56A0E8D7A8B5EABAECBB9 /* libPods-NeulandNext.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NeulandNext.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 4F50E0A33A0E4558A8B767F1 /* NeulandNext-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "NeulandNext-Bridging-Header.h"; path = "NeulandNext/NeulandNext-Bridging-Header.h"; sourceTree = ""; }; - A3A1D98E2B2A5F5DD26A73FF /* Pods-NeulandNext.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NeulandNext.debug.xcconfig"; path = "Target Support Files/Pods-NeulandNext/Pods-NeulandNext.debug.xcconfig"; sourceTree = ""; }; + 83AB1D9E2FAA015F64BDF1B1 /* Pods-NeulandNext.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NeulandNext.release.xcconfig"; path = "Target Support Files/Pods-NeulandNext/Pods-NeulandNext.release.xcconfig"; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = NeulandNext/SplashScreen.storyboard; sourceTree = ""; }; + AF143DA9DAE546BAA252E633 /* InfoPlist.strings */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = "en-US.lproj/InfoPlist.strings"; sourceTree = ""; }; B7A252912C646BAE009CD149 /* NeulandNextRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NeulandNextRelease.entitlements; path = NeulandNext/NeulandNextRelease.entitlements; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; - C0923420FA699D3B8ED8E097 /* Pods-NeulandNext.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NeulandNext.release.xcconfig"; path = "Target Support Files/Pods-NeulandNext/Pods-NeulandNext.release.xcconfig"; sourceTree = ""; }; + CA782F3247ED429475C0817A /* Pods-NeulandNext.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NeulandNext.debug.xcconfig"; path = "Target Support Files/Pods-NeulandNext/Pods-NeulandNext.debug.xcconfig"; sourceTree = ""; }; + DF16343C6EDDD0DDB9165116 /* libPods-NeulandNext.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NeulandNext.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-NeulandNext/ExpoModulesProvider.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -45,7 +49,7 @@ buildActionMask = 2147483647; files = ( B7E3797A2C54A52E006A4193 /* MapLibre in Frameworks */, - B66703B58F15AA5631B440D6 /* libPods-NeulandNext.a in Frameworks */, + 8F67CB1D1CD0F80933516397 /* libPods-NeulandNext.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -74,7 +78,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 39C56A0E8D7A8B5EABAECBB9 /* libPods-NeulandNext.a */, + DF16343C6EDDD0DDB9165116 /* libPods-NeulandNext.a */, ); name = Frameworks; sourceTree = ""; @@ -117,10 +121,20 @@ name = NeulandNext; sourceTree = ""; }; + 9E2D6BF4082F41509DE67D98 /* en-US.lproj */ = { + isa = PBXGroup; + children = ( + AF143DA9DAE546BAA252E633 /* InfoPlist.strings */, + ); + name = "en-US.lproj"; + sourceTree = ""; + }; BB2F792B24A3F905000567C9 /* Supporting */ = { isa = PBXGroup; children = ( BB2F792C24A3F905000567C9 /* Expo.plist */, + 9E2D6BF4082F41509DE67D98 /* en-US.lproj */, + ECA594B7E2814632BBDA7E25 /* de-DE.lproj */, ); name = Supporting; path = NeulandNext/Supporting; @@ -129,8 +143,8 @@ D65327D7A22EEC0BE12398D9 /* Pods */ = { isa = PBXGroup; children = ( - A3A1D98E2B2A5F5DD26A73FF /* Pods-NeulandNext.debug.xcconfig */, - C0923420FA699D3B8ED8E097 /* Pods-NeulandNext.release.xcconfig */, + CA782F3247ED429475C0817A /* Pods-NeulandNext.debug.xcconfig */, + 83AB1D9E2FAA015F64BDF1B1 /* Pods-NeulandNext.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -143,6 +157,14 @@ name = ExpoModulesProviders; sourceTree = ""; }; + ECA594B7E2814632BBDA7E25 /* de-DE.lproj */ = { + isa = PBXGroup; + children = ( + 15D289C40A50492AA788F5C8 /* InfoPlist.strings */, + ); + name = "de-DE.lproj"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -150,21 +172,16 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "NeulandNext" */; buildPhases = ( - 1DEDF007467C08465870B220 /* [CP] Check Pods Manifest.lock */, + FEE37F9A8B5F91F5E82374E8 /* [CP] Check Pods Manifest.lock */, 910811D19BBBDAE542453C80 /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 3563AF2B08A14DCAACDF586C /* Remove signature files (Xcode 15 workaround) */, - 02AF97E726504DDFAD70E08F /* Remove signature files (Xcode 15 workaround) */, - 051670571E954A2599A0996F /* Remove signature files (Xcode 15 workaround) */, - 9D89FFB4CACC4F44BFE94FCF /* Remove signature files (Xcode 15 workaround) */, - 7B622368697C4F4AAF1693D6 /* Remove signature files (Xcode 15 workaround) */, - DA8E3634621A409A8E362960 /* Remove signature files (Xcode 15 workaround) */, - B4C561E5297240E7ABBEA840 /* Remove signature files (Xcode 15 workaround) */, - 9BED70306DFC902D1A6A67BA /* [CP] Embed Pods Frameworks */, - FBD9C5931849757105D3088E /* [CP] Copy Pods Resources */, + B5D7FBC2AB8C412844A0CF8B /* [CP] Embed Pods Frameworks */, + AA761E64C35603CEBB2FA769 /* [CP] Copy Pods Resources */, + C6874FEF2F234D4A988A9C56 /* Remove signature files (Xcode 15 workaround) */, + FC608338A6A74B469AA88597 /* Remove signature files (Xcode 15 workaround) */, ); buildRules = ( ); @@ -221,6 +238,8 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, 79A805BC94BC0AD1674AEE08 /* PrivacyInfo.xcprivacy in Resources */, + D419EF2B714A439388DCB898 /* InfoPlist.strings in Resources */, + 5EA579CB98A042F5873E8D2B /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -242,104 +261,62 @@ shellPath = /bin/sh; shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; }; - 02AF97E726504DDFAD70E08F /* Remove signature files (Xcode 15 workaround) */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Remove signature files (Xcode 15 workaround)"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ \"$XCODE_VERSION_MAJOR\" = \"1500\" ]; then\n echo \"Remove signature files (Xcode 15 workaround)\";\n rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";\n fi"; - }; - 051670571E954A2599A0996F /* Remove signature files (Xcode 15 workaround) */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Remove signature files (Xcode 15 workaround)"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ \"$XCODE_VERSION_MAJOR\" = \"1500\" ]; then\n echo \"Remove signature files (Xcode 15 workaround)\";\n rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";\n fi"; - }; - 1DEDF007467C08465870B220 /* [CP] Check Pods Manifest.lock */ = { + 910811D19BBBDAE542453C80 /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Check Pods Manifest.lock"; + name = "[Expo] Configure project"; outputFileListPaths = ( ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-NeulandNext-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 3563AF2B08A14DCAACDF586C /* Remove signature files (Xcode 15 workaround) */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Remove signature files (Xcode 15 workaround)"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ \"$XCODE_VERSION_MAJOR\" = \"1500\" ]; then\n echo \"Remove signature files (Xcode 15 workaround)\";\n rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";\n fi"; - }; - 7B622368697C4F4AAF1693D6 /* Remove signature files (Xcode 15 workaround) */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Remove signature files (Xcode 15 workaround)"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ \"$XCODE_VERSION_MAJOR\" = \"1500\" ]; then\n echo \"Remove signature files (Xcode 15 workaround)\";\n rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";\n fi"; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-NeulandNext/expo-configure-project.sh\"\n"; }; - 910811D19BBBDAE542453C80 /* [Expo] Configure project */ = { + AA761E64C35603CEBB2FA769 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-NeulandNext/Pods-NeulandNext-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoLocalization/ExpoLocalization_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle", ); - name = "[Expo] Configure project"; - outputFileListPaths = ( - ); + name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoLocalization_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-NeulandNext/expo-configure-project.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NeulandNext/Pods-NeulandNext-resources.sh\"\n"; + showEnvVarsInLog = 0; }; - 9BED70306DFC902D1A6A67BA /* [CP] Embed Pods Frameworks */ = { + B5D7FBC2AB8C412844A0CF8B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -357,7 +334,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NeulandNext/Pods-NeulandNext-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 9D89FFB4CACC4F44BFE94FCF /* Remove signature files (Xcode 15 workaround) */ = { + C6874FEF2F234D4A988A9C56 /* Remove signature files (Xcode 15 workaround) */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -369,9 +346,9 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ \"$XCODE_VERSION_MAJOR\" = \"1500\" ]; then\n echo \"Remove signature files (Xcode 15 workaround)\";\n rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";\n fi"; + shellScript = "if [ \"$XCODE_VERSION_MAJOR\" = \"1500\" ]; then\n echo \"Remove signature files (Xcode 15 workaround)\";\n rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";\n fi\n"; }; - B4C561E5297240E7ABBEA840 /* Remove signature files (Xcode 15 workaround) */ = { + FC608338A6A74B469AA88597 /* Remove signature files (Xcode 15 workaround) */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -385,54 +362,26 @@ shellPath = /bin/sh; shellScript = "if [ \"$XCODE_VERSION_MAJOR\" = \"1500\" ]; then\n echo \"Remove signature files (Xcode 15 workaround)\";\n rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";\n fi"; }; - DA8E3634621A409A8E362960 /* Remove signature files (Xcode 15 workaround) */ = { + FEE37F9A8B5F91F5E82374E8 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( - ); - name = "Remove signature files (Xcode 15 workaround)"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ \"$XCODE_VERSION_MAJOR\" = \"1500\" ]; then\n echo \"Remove signature files (Xcode 15 workaround)\";\n rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";\n fi"; - }; - FBD9C5931849757105D3088E /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( + inputFileListPaths = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NeulandNext/Pods-NeulandNext-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoLocalization/ExpoLocalization_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoLocalization_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle", + "$(DERIVED_FILE_DIR)/Pods-NeulandNext-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NeulandNext/Pods-NeulandNext-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -454,7 +403,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A3A1D98E2B2A5F5DD26A73FF /* Pods-NeulandNext.debug.xcconfig */; + baseConfigurationReference = CA782F3247ED429475C0817A /* Pods-NeulandNext.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -500,7 +449,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C0923420FA699D3B8ED8E097 /* Pods-NeulandNext.release.xcconfig */; + baseConfigurationReference = 83AB1D9E2FAA015F64BDF1B1 /* Pods-NeulandNext.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -600,7 +549,10 @@ LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; @@ -659,7 +611,10 @@ ); LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/ios/NeulandNext.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/NeulandNext.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1f7263d4..d223fad1 100644 --- a/ios/NeulandNext.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/NeulandNext.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "e70d3525c8e2819a8b34f22909815dab5c700c25a06c32388f3930f7b3627768", + "originHash" : "b440cbd994821d1c59ef3fae39cdd359458004248a336c94f8a8b08f5fed3877", "pins" : [ { "identity" : "maplibre-gl-native-distribution", diff --git a/ios/NeulandNext/Info.plist b/ios/NeulandNext/Info.plist index 95d84c03..07b27194 100644 --- a/ios/NeulandNext/Info.plist +++ b/ios/NeulandNext/Info.plist @@ -1,174 +1,162 @@ - - CADisableMinimumFrameDurationOnPhone - - CFBundleAllowMixedLocalizations - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - Neuland Next - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIcons - - CFBundleAlternateIcons - - Default - - CFBundleIconFiles - - Default - - UIPrerenderedIcon - - - ModernDark - - CFBundleIconFiles - - ModernDark - - UIPrerenderedIcon - - - ModernGreen - - CFBundleIconFiles - - ModernGreen - - UIPrerenderedIcon - - - RainbowDark - - CFBundleIconFiles - - RainbowDark - - UIPrerenderedIcon - - - RainbowMoonLight - - CFBundleIconFiles - - RainbowMoonLight - - UIPrerenderedIcon - - - Retro - - CFBundleIconFiles - - Retro - - UIPrerenderedIcon - - - - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLocalizations - - en - de - - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 0.8.3 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - neuland - de.neuland-ingolstadt.neuland-app - - - - CFBundleURLSchemes - - exp+neuland-app-native - - - - CFBundleVersion - 71 - ITSAppUsesNonExemptEncryption - - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSFaceIDUsageDescription - Allow $(PRODUCT_NAME) to use Face ID. - NSLocationAlwaysAndWhenInUseUsageDescription - Allow $(PRODUCT_NAME) to access your location - NSLocationAlwaysUsageDescription - Allow $(PRODUCT_NAME) to access your location - NSLocationWhenInUseUsageDescription - Allow $(PRODUCT_NAME) to access your location. - NSUserActivityTypes - - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - - RCTAsyncStorageExcludeFromBackup - - UILaunchStoryboardName - SplashScreen - UIRequiredDeviceCapabilities - - arm64 - - UIRequiresFullScreen - - UIStatusBarStyle - UIStatusBarStyleDefault - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIUserInterfaceStyle - Automatic - UIViewControllerBasedStatusBarAppearance - - - + + CADisableMinimumFrameDurationOnPhone + + CFBundleAllowMixedLocalizations + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Neuland Next + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIcons + + CFBundleAlternateIcons + + Default + + CFBundleIconFiles + + Default + + UIPrerenderedIcon + + + ModernDark + + CFBundleIconFiles + + ModernDark + + UIPrerenderedIcon + + + ModernGreen + + CFBundleIconFiles + + ModernGreen + + UIPrerenderedIcon + + + RainbowDark + + CFBundleIconFiles + + RainbowDark + + UIPrerenderedIcon + + + RainbowMoonLight + + CFBundleIconFiles + + RainbowMoonLight + + UIPrerenderedIcon + + + Retro + + CFBundleIconFiles + + Retro + + UIPrerenderedIcon + + + + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLocalizations + + en + de + + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 0.9.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + neuland + de.neuland-ingolstadt.neuland-app + + + + CFBundleURLSchemes + + exp+neuland-app-native + + + + CFBundleVersion + 71 + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + NSFaceIDUsageDescription + Allow $(PRODUCT_NAME) to use Face ID. + NSLocationWhenInUseUsageDescription + Allow Neuland Next to access your location to show your location on the map. + NSUserActivityTypes + + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + + RCTAsyncStorageExcludeFromBackup + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + arm64 + + UIRequiresFullScreen + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIUserInterfaceStyle + Automatic + UIViewControllerBasedStatusBarAppearance + + + \ No newline at end of file diff --git a/ios/NeulandNext/NeulandNextRelease.entitlements b/ios/NeulandNext/NeulandNextRelease.entitlements index bddeabf7..4adf5e79 100644 --- a/ios/NeulandNext/NeulandNextRelease.entitlements +++ b/ios/NeulandNext/NeulandNextRelease.entitlements @@ -1,11 +1,11 @@ - - com.apple.developer.associated-domains - - webcredentials:neuland.app - activitycontinuation:neuland.app - - - + + com.apple.developer.associated-domains + + webcredentials:neuland.app + activitycontinuation:neuland.app + + + \ No newline at end of file diff --git a/ios/NeulandNext/Supporting/de-DE.lproj/InfoPlist.strings b/ios/NeulandNext/Supporting/de-DE.lproj/InfoPlist.strings new file mode 100644 index 00000000..d9b503f7 --- /dev/null +++ b/ios/NeulandNext/Supporting/de-DE.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +CFBundleDisplayName = "Neuland Next"; +NSFaceIDUsageDescription = "Erlaube Neuland Next die Verwendung von Face ID."; +NSLocationWhenInUseUsageDescription = "Erlaube Neuland Next den Zugriff auf deinen Standort, um deine Position auf der Karte anzuzeigen."; \ No newline at end of file diff --git a/ios/NeulandNext/Supporting/en-US.lproj/InfoPlist.strings b/ios/NeulandNext/Supporting/en-US.lproj/InfoPlist.strings new file mode 100644 index 00000000..ef007dd4 --- /dev/null +++ b/ios/NeulandNext/Supporting/en-US.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +CFBundleDisplayName = "Neuland Next"; +NSFaceIDUsageDescription = "Allow Neuland Next to use Face ID."; +NSLocationWhenInUseUsageDescription = "Allow Neuland Next to access your location to show your location on the map."; \ No newline at end of file diff --git a/ios/TestFlight/WhatToTest.de-DE.txt b/ios/TestFlight/WhatToTest.de-DE.txt index 6013f9f0..be65f272 100644 --- a/ios/TestFlight/WhatToTest.de-DE.txt +++ b/ios/TestFlight/WhatToTest.de-DE.txt @@ -1,11 +1,3 @@ -- Neues Onboarding und Login Design -- Neuer Suchverlauf bei Kartensuche -- Natives Design der erweiterten Raumsuche -- Verbesserte Kalender Performance -- Verbessertes Error Handling -- Verringert initale Ladezeit der App -- Verringert größe der App -- Behebt fehlende Übersetzungen -- Behebt Fehler beim Laden der Essensdaten -- Neue System Status Seite -- Neues Easter Egg \ No newline at end of file +- Neue Quick Links Karte +- Performance Verbesserungen +- Bugfixes \ No newline at end of file diff --git a/ios/TestFlight/WhatToTest.en-US.txt b/ios/TestFlight/WhatToTest.en-US.txt index 578eb3d0..de82ae83 100644 --- a/ios/TestFlight/WhatToTest.en-US.txt +++ b/ios/TestFlight/WhatToTest.en-US.txt @@ -1,11 +1 @@ -- New onboarding and login design -- New search history for map search -- Native design of the extended room search -- Improved calendar performance -- Improved error handling -- Reduced initial loading time of the app -- Reduced size of the app -- Fixes missing translations -- Fixes errors when loading the meal data -- New system status page -- New Easter Egg \ No newline at end of file +- New quick links \ No newline at end of file diff --git a/package.json b/package.json index af31e2cb..4344edde 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@material/material-color-utilities": "^0.3.0", "@react-native-community/datetimepicker": "8.0.1", "@react-native-community/netinfo": "11.3.1", + "@react-navigation/stack": "^6.4.1", "@shopify/flash-list": "1.6.4", "@tanstack/query-sync-storage-persister": "^5.51.21", "@tanstack/react-query": "^5.51.23", diff --git a/src/api/neuland-api.ts b/src/api/neuland-api.ts index bd798967..5b863d3b 100644 --- a/src/api/neuland-api.ts +++ b/src/api/neuland-api.ts @@ -3,7 +3,7 @@ import { gql, request } from 'graphql-request' import packageInfo from '../../package.json' -const GRAPHQL_ENDPOINT: string = 'https://api.dev.neuland.app/graphql' +const GRAPHQL_ENDPOINT: string = 'https://api.neuland.app/graphql' const ASSET_ENDPOINT: string = 'https://assets.neuland.app' const USER_AGENT = `neuland.app-native/${packageInfo.version} (+${packageInfo.homepage})` @@ -63,7 +63,7 @@ class NeulandAPIClient { } async getFoodPlan(locations: string[]): Promise { - const xx = await this.performGraphQLQuery(gql` + return await this.performGraphQLQuery(gql` query { food(locations: [${locations.map((x) => `"${x}"`).join(',')}]) { foodData { @@ -127,8 +127,6 @@ class NeulandAPIClient { } } `) - console.log(xx) - return xx } /** diff --git a/src/app/(flow)/onboarding.tsx b/src/app/(flow)/onboarding.tsx index ed5c62f9..a951347d 100644 --- a/src/app/(flow)/onboarding.tsx +++ b/src/app/(flow)/onboarding.tsx @@ -4,7 +4,7 @@ import LogoSVG from '@/components/Elements/Flow/svgs/logo' import LogoTextSVG from '@/components/Elements/Flow/svgs/logoText' import { type Colors } from '@/components/colors' import { FlowContext, UserKindContext } from '@/components/contexts' -import { PRIVACY_URL, USER_GUEST } from '@/utils/app-utils' +import { PRIVACY_URL, USER_GUEST } from '@/data/constants' import { getContrastColor } from '@/utils/ui-utils' import { useTheme } from '@react-navigation/native' import * as Haptics from 'expo-haptics' diff --git a/src/app/(flow)/whatsnew.tsx b/src/app/(flow)/whatsnew.tsx index 42cdf7d1..9417c2e2 100644 --- a/src/app/(flow)/whatsnew.tsx +++ b/src/app/(flow)/whatsnew.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react-hooks/rules-of-hooks */ import WhatsNewBox from '@/components/Elements/Flow/WhatsnewBox' import { type Colors } from '@/components/colors' import { FlowContext } from '@/components/contexts' @@ -7,38 +8,78 @@ import { type Changelog } from '@/types/data' import { convertToMajorMinorPatch } from '@/utils/app-utils' import { getContrastColor } from '@/utils/ui-utils' import { useTheme } from '@react-navigation/native' +import * as Application from 'expo-application' +import { ImpactFeedbackStyle, impactAsync } from 'expo-haptics' import { router } from 'expo-router' -import React, { useEffect, useState } from 'react' +import React, { useEffect } from 'react' import { useTranslation } from 'react-i18next' -import { Animated, Pressable, StyleSheet, Text, View } from 'react-native' - -import packageInfo from '../../../package.json' +import { Platform, Pressable, StyleSheet, Text, View } from 'react-native' +import Animated, { + Easing, + useAnimatedStyle, + useSharedValue, + withDelay, + withSequence, + withTiming, +} from 'react-native-reanimated' export default function WhatsNewScreen(): JSX.Element { const colors = useTheme().colors as Colors const flow = React.useContext(FlowContext) const changelog: Changelog = changelogData const { t, i18n } = useTranslation('flow') - const [opacityValues] = useState( - Object.keys( - changelog.version[convertToMajorMinorPatch(packageInfo.version)] ?? - [] - ) - .flatMap((key) => changelog.version[key]) - .map(() => new Animated.Value(0)) + const version = convertToMajorMinorPatch( + Application.nativeApplicationVersion ?? '0.0.0' ) - useEffect(() => { - const delay = 100 + const totalItems = Object.keys(changelog.version[version] ?? []).flatMap( + (key) => changelog.version[key] + ).length + const opacityValues = changelog.version[version].map(() => + useSharedValue(0) + ) + const rotationValues = Array.from({ length: totalItems }, () => + useSharedValue(0) + ) + + const handlePress = (index: number): void => { + if (Platform.OS === 'ios') { + void impactAsync(ImpactFeedbackStyle.Light) + } + const direction = Math.random() > 0.5 ? 1 : -1 + const rotation = rotationValues[index] + rotation.value = withSequence( + withTiming(direction * -1.5, { + duration: 100, + easing: Easing.linear, + }), + withTiming(direction * 1, { + duration: 100, + easing: Easing.linear, + }), + withTiming(direction * -0.5, { + duration: 100, + easing: Easing.linear, + }), + withTiming(0, { + duration: 100, + easing: Easing.linear, + }) + ) + } + + useEffect(() => { + const delay = 200 setTimeout(() => { - const animations = opacityValues.map((opacity, index) => - Animated.timing(opacity, { - toValue: 1, - duration: 800, - useNativeDriver: true, - }) - ) - Animated.stagger(425, animations).start() + opacityValues.forEach((opacity, index) => { + opacity.value = withDelay( + index * 440, + withTiming(1, { + duration: 800, + easing: Easing.linear, + }) + ) + }) }, delay) }, []) @@ -64,51 +105,79 @@ export default function WhatsNewScreen(): JSX.Element { ]} > {t('whatsnew.version', { - version: convertToMajorMinorPatch(packageInfo.version), + version, })} {Object.keys(changelog.version) - .filter( - (key) => - key === - convertToMajorMinorPatch(packageInfo.version) - ) + .filter((key) => key === version) .map((key, boxIndex) => ( {changelog.version[key].map( - ({ title, description, icon }, index) => ( - { + const overallIndex = + boxIndex * + changelog.version[key].length + + index + + const opacityStyle = useAnimatedStyle( + () => { + return { + opacity: + opacityValues[overallIndex] + .value, + } } - style={{ - opacity: - opacityValues[ - boxIndex * - changelog.version[key] - .length + - index + ) + + const rotationStyle = useAnimatedStyle( + () => { + return { + transform: [ + { + rotateZ: `${rotationValues[overallIndex].value}deg`, + }, ], - }} - > - - - ) + style={[ + opacityStyle, + rotationStyle, + ]} + > + { + handlePress(overallIndex) + }} + > + + + + ) + } )} ))} diff --git a/src/app/(screens)/about.tsx b/src/app/(screens)/about.tsx index 1e979fe7..f8a35760 100644 --- a/src/app/(screens)/about.tsx +++ b/src/app/(screens)/about.tsx @@ -4,8 +4,8 @@ import SectionView from '@/components/Elements/Universal/SectionsView' import SingleSectionPicker from '@/components/Elements/Universal/SingleSectionPicker' import { type Colors } from '@/components/colors' import { FlowContext, PreferencesContext } from '@/components/contexts' +import { IMPRINT_URL, PRIVACY_URL, STATUS_URL } from '@/data/constants' import { type FormListSections } from '@/types/components' -import { IMPRINT_URL, PRIVACY_URL, STATUS_URL } from '@/utils/app-utils' import { PAGE_BOTTOM_SAFE_AREA, PAGE_PADDING } from '@/utils/style-utils' import { trackEvent } from '@aptabase/react-native' import { useTheme } from '@react-navigation/native' @@ -61,17 +61,6 @@ export default function About(): JSX.Element { router.navigate('changelog') }, }, - { - title: 'Feedback', - icon: { - ios: 'envelope', - android: 'mail', - }, - onPress: async () => - await Linking.openURL( - 'mailto:app-feedback@informatik.sexy?subject=Feedback%20Neuland-Next' - ), - }, { title: 'System Status', icon: { diff --git a/src/app/(screens)/accent.tsx b/src/app/(screens)/accent.tsx new file mode 100644 index 00000000..c5661eed --- /dev/null +++ b/src/app/(screens)/accent.tsx @@ -0,0 +1,186 @@ +import PlatformIcon from '@/components/Elements/Universal/Icon' +import SectionView from '@/components/Elements/Universal/SectionsView' +import { type Colors, accentColors } from '@/components/colors' +import { ThemeContext } from '@/components/contexts' +import { getContrastColor } from '@/utils/ui-utils' +import { useTheme } from '@react-navigation/native' +import * as Haptics from 'expo-haptics' +import React, { useContext } from 'react' +import { useTranslation } from 'react-i18next' +import { + Platform, + Pressable, + ScrollView, + StyleSheet, + Text, + View, +} from 'react-native' + +export default function Theme(): JSX.Element { + const colors = useTheme().colors as Colors + const deviceTheme = useTheme() + const { accentColor, setAccentColor } = useContext(ThemeContext) + const { t } = useTranslation(['settings']) + + interface ColorBoxColor { + light: string + dark: string + } + + const ColorBox = ({ + color, + code, + }: { + color: ColorBoxColor + code: string + }): JSX.Element => { + const themeAccentColor = deviceTheme.dark ? color.dark : color.light + return ( + + { + setAccentColor(code) + if (Platform.OS === 'ios') { + void Haptics.selectionAsync() + } + }} + style={({ pressed }) => [ + { + opacity: pressed ? 0.8 : 1, + marginHorizontal: 15, + }, + ]} + accessibilityLabel={t( + // @ts-expect-error cannot verify that code is a valid key + `theme.colors.${code}` + )} + > + + {accentColor === code && ( + + )} + + + + {/* @ts-expect-error cannot verify that code is a valid key */} + {t(`theme.colors.${code}`)} + + + ) + } + + interface ColorBoxMatrixProps { + colors: Array<{ + code: string + color: ColorBoxColor + }> + } + + const ColorBoxMatrix = ({ colors }: ColorBoxMatrixProps): JSX.Element => { + return ( + + {colors.map((color, index) => ( + + ))} + + ) + } + + const colorRows = Array.from({ length: 3 }, (_, rowIndex) => + Object.entries(accentColors) + .slice(rowIndex * 3, (rowIndex + 1) * 3) + .map(([key, value]) => ({ + code: key, + color: value, + })) + ) + + return ( + <> + + + + {colorRows.map((rowColors, index) => ( + + ))} + + + + + ) +} + +const styles = StyleSheet.create({ + colorBox: { + width: 60, + height: 60, + borderRadius: 4, + justifyContent: 'center', + alignItems: 'center', + alignContent: 'center', + flexDirection: 'row', + borderWidth: 2, + borderTopLeftRadius: 10, + borderTopRightRadius: 10, + borderBottomLeftRadius: 10, + borderBottomRightRadius: 10, + }, + sectionContainer: { + borderRadius: 8, + alignContent: 'center', + justifyContent: 'center', + flexDirection: 'column', + flexWrap: 'wrap', + paddingVertical: 18, + }, + colorBoxContainer: { + justifyContent: 'center', + }, + colorBoxText: { + textAlign: 'center', + paddingTop: 4, + }, + colorMatrixContainer: { + flexDirection: 'row', + justifyContent: 'center', + marginVertical: 2, + paddingVertical: 4, + }, +}) diff --git a/src/app/(screens)/calendar.tsx b/src/app/(screens)/calendar.tsx index e9b6f393..fa6c5a16 100644 --- a/src/app/(screens)/calendar.tsx +++ b/src/app/(screens)/calendar.tsx @@ -5,9 +5,9 @@ import Divider from '@/components/Elements/Universal/Divider' import ToggleRow from '@/components/Elements/Universal/ToggleRow' import { type Colors } from '@/components/colors' import { UserKindContext } from '@/components/contexts' +import { USER_GUEST } from '@/data/constants' import { useRefreshByUser } from '@/hooks' import { guestError, networkError } from '@/utils/api-utils' -import { USER_GUEST } from '@/utils/app-utils' import { calendar, loadExamList } from '@/utils/calendar-utils' import { PAGE_PADDING } from '@/utils/style-utils' import { useTheme } from '@react-navigation/native' diff --git a/src/app/(screens)/dashboard.tsx b/src/app/(screens)/dashboard.tsx index d68e2c9c..d4b6e31f 100644 --- a/src/app/(screens)/dashboard.tsx +++ b/src/app/(screens)/dashboard.tsx @@ -5,8 +5,9 @@ import { type Colors } from '@/components/colors' import { DashboardContext, UserKindContext } from '@/components/contexts' import { cardIcons } from '@/components/icons' import { getDefaultDashboardOrder } from '@/contexts/dashboard' +import { USER_GUEST } from '@/data/constants' import { type MaterialIcon } from '@/types/material-icons' -import { USER_GUEST, arraysEqual } from '@/utils/app-utils' +import { arraysEqual } from '@/utils/app-utils' import { PAGE_PADDING } from '@/utils/style-utils' import { showToast } from '@/utils/ui-utils' import { useTheme } from '@react-navigation/native' diff --git a/src/app/(screens)/grades.tsx b/src/app/(screens)/grades.tsx index 9fb00044..967d3155 100644 --- a/src/app/(screens)/grades.tsx +++ b/src/app/(screens)/grades.tsx @@ -86,9 +86,8 @@ export default function GradesSCreen(): JSX.Element { retry(failureCount, error) { if (error instanceof NoSessionError) { router.replace('user/login') - return false } - return failureCount < 3 + return false }, }) @@ -101,13 +100,15 @@ export default function GradesSCreen(): JSX.Element { const { isRefetchingByUser, refetchByUser } = useRefreshByUser(refetch) useEffect(() => { + if (personalData === undefined) return const spoName = extractSpoName(personalData) - void loadAverageGrade(spoName) + void loadAverageGrade(spoName ?? undefined) }, [spoWeights, grades?.finished]) return ( )} {isError && ( - + + + )} {isPaused && !isSuccess && ( { - if (data?.po_url !== undefined && data.po_url !== '') { + if ( + data?.po_url !== undefined && + data.po_url !== '' && + data.po_url !== 'http://www.thi.de' + ) { void Linking.openURL(data.po_url) } }, diff --git a/src/app/(screens)/settings.tsx b/src/app/(screens)/settings.tsx index c7b55a80..d34125c1 100644 --- a/src/app/(screens)/settings.tsx +++ b/src/app/(screens)/settings.tsx @@ -7,6 +7,7 @@ import { type Colors } from '@/components/colors' import { DashboardContext, UserKindContext } from '@/components/contexts' import { queryClient } from '@/components/provider' import { type UserKindContextType } from '@/contexts/userKind' +import { USER_EMPLOYEE, USER_GUEST, USER_STUDENT } from '@/data/constants' import { useRefreshByUser } from '@/hooks' import { type FormListSections } from '@/types/components' import { type MaterialIcon } from '@/types/material-icons' @@ -16,7 +17,6 @@ import { withBouncing, } from '@/utils/animation-utils' import { getPersonalData, performLogout } from '@/utils/api-utils' -import { USER_EMPLOYEE, USER_GUEST, USER_STUDENT } from '@/utils/app-utils' import { storage } from '@/utils/storage' import { getContrastColor, getInitials } from '@/utils/ui-utils' import { trackEvent } from '@aptabase/react-native' @@ -254,11 +254,21 @@ export default function Settings(): JSX.Element { header: t('menu.formlist.appearance.title'), items: [ { - title: t('menu.formlist.appearance.theme'), + title: t('menu.formlist.appearance.accent'), icon: { ios: 'paintpalette', android: 'palette', }, + onPress: () => { + router.navigate('accent') + }, + }, + { + title: t('menu.formlist.appearance.theme'), + icon: { + ios: 'moon.stars', + android: 'routine', + }, onPress: () => { router.navigate('theme') }, @@ -279,27 +289,6 @@ export default function Settings(): JSX.Element { : []), ], }, - - // { - // header: 'Quick Links', - // items: [ - // { - // title: 'Primuss', - // icon: linkIcon, - // onPress: async () => await Linking.openURL(primussLink), - // }, - // { - // title: 'Moodle', - // icon: linkIcon, - // onPress: async () => await Linking.openURL(moodleLink), - // }, - // { - // title: 'Webmail', - // icon: linkIcon, - // onPress: async () => await Linking.openURL(mailLink), - // }, - // ], - // }, { header: t('menu.formlist.legal.title'), items: [ diff --git a/src/app/(screens)/theme.tsx b/src/app/(screens)/theme.tsx index 49389689..80dbad13 100644 --- a/src/app/(screens)/theme.tsx +++ b/src/app/(screens)/theme.tsx @@ -1,129 +1,13 @@ import MultiSectionRadio from '@/components/Elements/Food/FoodLanguageSection' -import PlatformIcon from '@/components/Elements/Universal/Icon' import SectionView from '@/components/Elements/Universal/SectionsView' -import { type Colors, accentColors } from '@/components/colors' import { ThemeContext } from '@/components/contexts' -import { getContrastColor } from '@/utils/ui-utils' -import { useTheme } from '@react-navigation/native' -import * as Haptics from 'expo-haptics' import React, { useContext } from 'react' import { useTranslation } from 'react-i18next' -import { - Platform, - Pressable, - ScrollView, - StyleSheet, - Text, - View, -} from 'react-native' export default function Theme(): JSX.Element { - const colors = useTheme().colors as Colors - const deviceTheme = useTheme() - const { accentColor, setAccentColor, theme, setTheme } = - useContext(ThemeContext) + const { theme, setTheme } = useContext(ThemeContext) const { t } = useTranslation(['settings']) - interface ColorBoxColor { - light: string - dark: string - } - - const ColorBox = ({ - color, - code, - }: { - color: ColorBoxColor - code: string - }): JSX.Element => { - const themeAccentColor = deviceTheme.dark ? color.dark : color.light - return ( - - { - setAccentColor(code) - if (Platform.OS === 'ios') { - void Haptics.selectionAsync() - } - }} - style={({ pressed }) => [ - { - opacity: pressed ? 0.8 : 1, - marginHorizontal: 15, - }, - ]} - accessibilityLabel={t( - // @ts-expect-error cannot verify that code is a valid key - `theme.colors.${code}` - )} - > - - {accentColor === code && ( - - )} - - - - {/* @ts-expect-error cannot verify that code is a valid key */} - {t(`theme.colors.${code}`)} - - - ) - } - - interface ColorBoxMatrixProps { - colors: Array<{ - code: string - color: ColorBoxColor - }> - } - - const ColorBoxMatrix = ({ colors }: ColorBoxMatrixProps): JSX.Element => { - return ( - - {colors.map((color, index) => ( - - ))} - - ) - } - - const colorRows = Array.from({ length: 3 }, (_, rowIndex) => - Object.entries(accentColors) - .slice(rowIndex * 3, (rowIndex + 1) * 3) - .map(([key, value]) => ({ - code: key, - color: value, - })) - ) - const elements = [ { key: 'auto', @@ -141,70 +25,16 @@ export default function Theme(): JSX.Element { return ( <> - - - - {colorRows.map((rowColors, index) => ( - - ))} - - - - void} - /> - - + + void} + /> + ) } - -const styles = StyleSheet.create({ - colorBox: { - width: 60, - height: 60, - borderRadius: 4, - justifyContent: 'center', - alignItems: 'center', - alignContent: 'center', - flexDirection: 'row', - borderWidth: 2, - borderTopLeftRadius: 10, - borderTopRightRadius: 10, - borderBottomLeftRadius: 10, - borderBottomRightRadius: 10, - }, - sectionContainer: { - borderRadius: 8, - alignContent: 'center', - justifyContent: 'center', - flexDirection: 'column', - flexWrap: 'wrap', - paddingVertical: 18, - }, - colorBoxContainer: { - justifyContent: 'center', - }, - colorBoxText: { - textAlign: 'center', - paddingTop: 4, - }, - colorMatrixContainer: { - flexDirection: 'row', - justifyContent: 'center', - marginVertical: 2, - paddingVertical: 4, - }, -}) diff --git a/src/app/(tabs)/(index)/_layout.tsx b/src/app/(tabs)/(index)/_layout.tsx index 889fe038..7b2fdf95 100644 --- a/src/app/(tabs)/(index)/_layout.tsx +++ b/src/app/(tabs)/(index)/_layout.tsx @@ -1,33 +1,41 @@ import BottomSheet from '@/components/Elements/Layout/BottomSheet' import BottomSheetRootBackground from '@/components/Elements/Universal/BottomSheetRootBackground' +import { type Colors } from '@/components/colors' import { BottomSheetBackdrop, type BottomSheetBackdropProps, } from '@gorhom/bottom-sheet' import '@react-navigation/elements' +import { useTheme } from '@react-navigation/native' import { Slot } from 'expo-router' -import React, { useRef } from 'react' +import React from 'react' // eslint-disable-next-line @typescript-eslint/naming-convention export const unstable_settings = { initialRouteName: 'index', } const renderBackdrop = (props: BottomSheetBackdropProps): JSX.Element => ( - + ) export default function Layout(): JSX.Element { - const ref = useRef(null) + const colors = useTheme().colors as Colors if (typeof window === 'undefined') return return ( , + handleIndicatorStyle: { + backgroundColor: colors.labelSecondaryColor, + }, backdropComponent: renderBackdrop, - stackBehavior: 'replace', enableDynamicSizing: true, }} diff --git a/src/app/(tabs)/(index)/index.tsx b/src/app/(tabs)/(index)/index.tsx index f2be7700..1a4343ca 100644 --- a/src/app/(tabs)/(index)/index.tsx +++ b/src/app/(tabs)/(index)/index.tsx @@ -9,9 +9,9 @@ import { PAGE_BOTTOM_SAFE_AREA, PAGE_PADDING } from '@/utils/style-utils' import { useTheme } from '@react-navigation/native' import { MasonryFlashList } from '@shopify/flash-list' import { useQuery } from '@tanstack/react-query' -import { SplashScreen, router, useNavigation } from 'expo-router' +import { router, useNavigation } from 'expo-router' import Head from 'expo-router/head' -import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react' +import React, { useEffect, useLayoutEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { Dimensions, @@ -21,8 +21,6 @@ import { View, } from 'react-native' -void SplashScreen.hideAsync() - export default function HomeRootScreen(): JSX.Element { const [isPageOpen, setIsPageOpen] = useState(false) @@ -52,6 +50,7 @@ export default function HomeRootScreen(): JSX.Element { largeTitle={true} transparent={true} headerRightElement={IndexHeaderRight} + androidFallback /> @@ -111,45 +110,6 @@ function HomeScreen(): JSX.Element { }) }, [isCollapsed, colors.card]) - const FlashList = useMemo(() => { - return ( - { - let paddingStyle = {} - - if (columns !== 1) { - paddingStyle = - index % 2 === 0 - ? { paddingRight: PAGE_PADDING / 2 } - : { paddingLeft: PAGE_PADDING / 2 } - } - - return ( - - {item.card()} - - ) - }} - keyExtractor={(item) => item.key} - numColumns={columns} - estimatedItemSize={114} - ListHeaderComponent={() => - data !== undefined ? ( - - ) : ( - <> - ) - } - /> - ) - }, [columns, data, orientation, shownDashboardEntries]) - return shownDashboardEntries === null || shownDashboardEntries.length === 0 ? ( @@ -169,7 +129,40 @@ function HomeScreen(): JSX.Element { /> ) : ( - FlashList + { + let paddingStyle = {} + + if (columns !== 1) { + paddingStyle = + index % 2 === 0 + ? { paddingRight: PAGE_PADDING / 2 } + : { paddingLeft: PAGE_PADDING / 2 } + } + + return ( + + {item.card()} + + ) + }} + keyExtractor={(item) => item.key} + numColumns={columns} + estimatedItemSize={114} + ListHeaderComponent={() => + data !== undefined ? ( + + ) : ( + <> + ) + } + /> ) } diff --git a/src/app/(tabs)/(index)/links.tsx b/src/app/(tabs)/(index)/links.tsx index 0ae0ca66..b68485bb 100644 --- a/src/app/(tabs)/(index)/links.tsx +++ b/src/app/(tabs)/(index)/links.tsx @@ -1,17 +1,16 @@ import FormList from '@/components/Elements/Universal/FormList' -import PlatformIcon from '@/components/Elements/Universal/Icon' import { type Colors } from '@/components/colors' import { PreferencesContext } from '@/components/contexts' +import { quicklinks } from '@/data/constants' import { type FormListSections } from '@/types/components' import { type MaterialIcon } from '@/types/material-icons' -import { quicklinks } from '@/utils/app-utils' import { PAGE_PADDING } from '@/utils/style-utils' import { trackEvent } from '@aptabase/react-native' import { useTheme } from '@react-navigation/native' import { router } from 'expo-router' import React, { useContext } from 'react' import { useTranslation } from 'react-i18next' -import { Linking, Pressable, StyleSheet, Text, View } from 'react-native' +import { Linking, StyleSheet, Text, View } from 'react-native' const LinkScreen = (): JSX.Element => { const colors = useTheme().colors as Colors @@ -40,7 +39,8 @@ const LinkScreen = (): JSX.Element => { title: // @ts-expect-error Type cannot be verified t(['pages.quicklinks.links.' + link.key, link.key]), - icon: link.icon, + icon: { ...link.icon, androidVariant: 'outlined' }, + onPress: async () => { await linkPress(link.key, link.url) }, @@ -63,21 +63,6 @@ const LinkScreen = (): JSX.Element => { > {t('pages.quicklinks.title')} - { - router.back() - }} - style={styles.xmark} - > - - { } const styles = StyleSheet.create({ - formlistRow: { marginVertical: 15 }, + formlistRow: { marginVertical: 14.5 }, headerContainer: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: PAGE_PADDING, }, - xmark: { marginTop: -10, padding: 6 }, headerText: { - fontSize: 24, - fontWeight: 'bold', - paddingTop: 10, + fontSize: 23, + fontWeight: '600', + paddingTop: 5, }, }) diff --git a/src/app/(tabs)/(timetable)/timetable.tsx b/src/app/(tabs)/(timetable)/timetable.tsx index 5b2c603f..eaf57199 100644 --- a/src/app/(tabs)/(timetable)/timetable.tsx +++ b/src/app/(tabs)/(timetable)/timetable.tsx @@ -3,10 +3,10 @@ import TimetableList from '@/components/Elements/Timetable/TimetableList' import TimetableWeek from '@/components/Elements/Timetable/TimetableWeek' import { type Colors } from '@/components/colors' import { PreferencesContext, UserKindContext } from '@/components/contexts' +import { USER_GUEST } from '@/data/constants' import { useRefreshByUser } from '@/hooks' import { type Exam, type FriendlyTimetableEntry } from '@/types/utils' import { guestError, networkError } from '@/utils/api-utils' -import { USER_GUEST } from '@/utils/app-utils' import { loadExamList } from '@/utils/calendar-utils' import { getFriendlyTimetable } from '@/utils/timetable-utils' import { useTheme } from '@react-navigation/native' diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index b31a90a1..106e001e 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -9,13 +9,14 @@ import { UserKindContext, } from '@/components/contexts' import changelog from '@/data/changelog.json' -import { USER_GUEST, convertToMajorMinorPatch } from '@/utils/app-utils' +import { USER_GUEST } from '@/data/constants' +import { convertToMajorMinorPatch } from '@/utils/app-utils' import Aptabase from '@aptabase/react-native' import { type Theme, useTheme } from '@react-navigation/native' import Color from 'color' +import * as Application from 'expo-application' import * as NavigationBar from 'expo-navigation-bar' import { Redirect, usePathname, useRouter } from 'expo-router' -import * as SplashScreen from 'expo-splash-screen' import React, { useContext, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { Platform } from 'react-native' @@ -24,9 +25,6 @@ import Shortcuts, { type ShortcutItem } from 'rn-quick-actions' import { appIcons } from '../(screens)/appIcon' import { humanLocations } from '../(screens)/meal' -import packageInfo from '../../../package.json' - -void SplashScreen.preventAutoHideAsync() declare const process: { env: { @@ -184,17 +182,20 @@ export default function HomeLayout(): JSX.Element { return } - const isChangelogAvailable = Object.keys(changelog.version).some( - (version) => version === convertToMajorMinorPatch(packageInfo.version) - ) + const version = Application.nativeApplicationVersion + const isChangelogAvailable = + version != null + ? Object.keys(changelog.version).some( + (version) => version === convertToMajorMinorPatch(version) + ) + : false if ( - flow.isUpdated === false && + flow.isUpdated !== true && isChangelogAvailable && - flow.isOnboarded !== false + flow.isOnboarded === true ) { router.navigate('(flow)/whatsnew') - void SplashScreen.hideAsync() } return Platform.OS === 'android' ? ( diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index ea20492f..d3c0b079 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -17,7 +17,7 @@ import { AppState, Platform, Pressable, useColorScheme } from 'react-native' // eslint-disable-next-line @typescript-eslint/naming-convention export const unstable_settings = { - initialRouteName: '(tabs)/(index)', + initialRouteName: '(index)', } function RootLayout(): JSX.Element { const router = useRouter() @@ -220,7 +220,13 @@ function RootLayout(): JSX.Element { animation: 'slide_from_right', }} /> - + @@ -23,6 +25,13 @@ export default function App(): JSX.Element { + ) } + +const styles = StyleSheet.create({ + page: { + flex: 1, + }, +}) diff --git a/src/components/Cards/BaseCard.tsx b/src/components/Cards/BaseCard.tsx index f43ee369..c4d6c0dc 100644 --- a/src/components/Cards/BaseCard.tsx +++ b/src/components/Cards/BaseCard.tsx @@ -1,7 +1,7 @@ // BaseCard Component to show the card on the dashboard to navigate to the corresponding page import { type Colors } from '@/components/colors' +import { USER_GUEST } from '@/data/constants' import { type MaterialIcon } from '@/types/material-icons' -import { USER_GUEST } from '@/utils/app-utils' import { CARD_PADDING, PAGE_PADDING } from '@/utils/style-utils' import { useTheme } from '@react-navigation/native' import { router } from 'expo-router' diff --git a/src/components/Cards/CalendarCard.tsx b/src/components/Cards/CalendarCard.tsx index 72a40a3d..6e8d22b7 100644 --- a/src/components/Cards/CalendarCard.tsx +++ b/src/components/Cards/CalendarCard.tsx @@ -2,9 +2,9 @@ import { NoSessionError } from '@/api/thi-session-handler' import Divider from '@/components/Elements/Universal/Divider' import { type Colors } from '@/components/colors' import { FlowContext, UserKindContext } from '@/components/contexts' +import { USER_GUEST, USER_STUDENT } from '@/data/constants' import { type LanguageKey } from '@/localization/i18n' import { type Calendar } from '@/types/data' -import { USER_GUEST, USER_STUDENT } from '@/utils/app-utils' import { calendar, loadExamList } from '@/utils/calendar-utils' import { formatFriendlyRelativeTime } from '@/utils/date-utils' import { useTheme } from '@react-navigation/native' diff --git a/src/components/Cards/FoodCard.tsx b/src/components/Cards/FoodCard.tsx index 8cf848c9..bc0bbebc 100644 --- a/src/components/Cards/FoodCard.tsx +++ b/src/components/Cards/FoodCard.tsx @@ -1,8 +1,8 @@ import Divider from '@/components/Elements/Universal/Divider' import { type Colors } from '@/components/colors' import { FoodFilterContext, UserKindContext } from '@/components/contexts' +import { USER_GUEST } from '@/data/constants' import { type LanguageKey } from '@/localization/i18n' -import { USER_GUEST } from '@/utils/app-utils' import { formatISODate } from '@/utils/date-utils' import { getUserSpecificPrice, @@ -39,7 +39,6 @@ const FoodCard = (): JSX.Element => { gcTime: 1000 * 60 * 60 * 24, // 24 hourss }) - console.log('rendering FoodCard') useEffect(() => { if (!isSuccess) { // if data is not loaded yet, do nothing diff --git a/src/components/Cards/LinkCard.tsx b/src/components/Cards/LinkCard.tsx index c6ed9d5d..9a58d0f3 100644 --- a/src/components/Cards/LinkCard.tsx +++ b/src/components/Cards/LinkCard.tsx @@ -1,7 +1,7 @@ import { type Colors } from '@/components/colors' import { PreferencesContext } from '@/components/contexts' +import { quicklinks } from '@/data/constants' import { type MaterialIcon } from '@/types/material-icons' -import { quicklinks } from '@/utils/app-utils' import { trackEvent } from '@aptabase/react-native' import { useTheme } from '@react-navigation/native' import React, { useContext } from 'react' @@ -44,7 +44,8 @@ const LinkCard = (): JSX.Element => { }} android={{ name: link.icon.android as MaterialIcon, - size: 24, + size: 20, + variant: 'outlined', }} /> { const [showLoadingIndicator, setShowLoadingIndicator] = useState(false) const [initials, setInitials] = useState('') + const androidPadding = Platform.OS === 'android' ? 16 : 0 const { data: persData, @@ -127,7 +128,7 @@ export const IndexHeaderRight = (): JSX.Element => { ] : []), { - title: t('navigation.theme'), + title: t('navigation.accent'), systemIcon: Platform.OS === 'ios' ? 'paintpalette' : undefined, }, @@ -165,8 +166,8 @@ export const IndexHeaderRight = (): JSX.Element => { onPress={(e) => { if (e.nativeEvent.name === t('navigation.profile')) { router.push('profile') - } else if (e.nativeEvent.name === t('navigation.theme')) { - router.push('theme') + } else if (e.nativeEvent.name === t('navigation.accent')) { + router.push('accent') } else if (e.nativeEvent.name === t('navigation.about')) { router.push('about') } else if (e.nativeEvent.name === 'Logout') { @@ -194,6 +195,9 @@ export const IndexHeaderRight = (): JSX.Element => { delayLongPress={300} onLongPress={() => {}} accessibilityLabel={t('navigation.settings')} + style={{ + paddingRight: androidPadding, + }} > {userKind === USER_EMPLOYEE ? ( diff --git a/src/components/Elements/Error/ActionButtons.tsx b/src/components/Elements/Error/ActionButtons.tsx index ddb906f0..23efd2a0 100644 --- a/src/components/Elements/Error/ActionButtons.tsx +++ b/src/components/Elements/Error/ActionButtons.tsx @@ -1,5 +1,5 @@ import { type Colors } from '@/components/colors' -import { STATUS_URL } from '@/utils/app-utils' +import { STATUS_URL } from '@/data/constants' import { useTheme } from '@react-navigation/native' import * as Application from 'expo-application' import React from 'react' diff --git a/src/components/Elements/Food/MealEntry.tsx b/src/components/Elements/Food/MealEntry.tsx index a22f98be..c784a4b4 100644 --- a/src/components/Elements/Food/MealEntry.tsx +++ b/src/components/Elements/Food/MealEntry.tsx @@ -2,9 +2,9 @@ import { humanLocations } from '@/app/(screens)/meal' import { type Colors } from '@/components/colors' import { FoodFilterContext, UserKindContext } from '@/components/contexts' import { type UserKindContextType } from '@/contexts/userKind' +import { USER_GUEST } from '@/data/constants' import { type LanguageKey } from '@/localization/i18n' import { type Meal } from '@/types/neuland-api' -import { USER_GUEST } from '@/utils/app-utils' import { convertRelevantAllergens, convertRelevantFlags, diff --git a/src/components/Elements/Map/BottomSheetMap.tsx b/src/components/Elements/Map/BottomSheetMap.tsx index 3680ffef..54da83bc 100644 --- a/src/components/Elements/Map/BottomSheetMap.tsx +++ b/src/components/Elements/Map/BottomSheetMap.tsx @@ -1,8 +1,8 @@ import { type Colors } from '@/components/colors' import { PreferencesContext, UserKindContext } from '@/components/contexts' import { MapContext } from '@/contexts/map' +import { USER_GUEST } from '@/data/constants' import { SEARCH_TYPES, type SearchResult } from '@/types/map' -import { USER_GUEST } from '@/utils/app-utils' import { formatFriendlyDate, formatFriendlyTime } from '@/utils/date-utils' import { PAGE_BOTTOM_SAFE_AREA, PAGE_PADDING } from '@/utils/style-utils' import { getContrastColor, showToast } from '@/utils/ui-utils' diff --git a/src/components/Elements/Map/MapScreen.tsx b/src/components/Elements/Map/MapScreen.tsx index a72ccffa..42d55635 100644 --- a/src/components/Elements/Map/MapScreen.tsx +++ b/src/components/Elements/Map/MapScreen.tsx @@ -12,10 +12,10 @@ import FloorPicker from '@/components/Elements/Map/FloorPicker' import { type Colors } from '@/components/colors' import { RouteParamsContext, UserKindContext } from '@/components/contexts' import { MapContext } from '@/contexts/map' +import { USER_GUEST } from '@/data/constants' import { type FeatureProperties, Gebaeude } from '@/types/asset-api' import { type RoomData, SEARCH_TYPES } from '@/types/map' import { type FriendlyTimetableEntry } from '@/types/utils' -import { USER_GUEST } from '@/utils/app-utils' import { formatISODate, formatISOTime } from '@/utils/date-utils' import { BUILDINGS, diff --git a/src/components/Elements/Timetable/TimetableWeek.tsx b/src/components/Elements/Timetable/TimetableWeek.tsx index a60d1d1a..649eb1b6 100644 --- a/src/components/Elements/Timetable/TimetableWeek.tsx +++ b/src/components/Elements/Timetable/TimetableWeek.tsx @@ -24,8 +24,6 @@ import WeekView, { import { HeaderLeft, HeaderRight } from './HeaderButtons' -// Import HeaderComponentProps - export default function TimetableWeek({ // eslint-disable-next-line react/prop-types friendlyTimetable, @@ -37,6 +35,11 @@ export default function TimetableWeek({ const { selectedDate, setSelectedDate } = useContext(PreferencesContext) // get the first day of friendlyTimetable that is not in the past const today = new Date() + const firstElementeDate = friendlyTimetable.find( + (entry: FriendlyTimetableEntry) => new Date(entry.startDate) > today + )?.startDate + const [localSelectedDate, setLocalSelectedDate] = + React.useState(selectedDate) const inversePrimary = inverseColor(colors.primary) const friendlyTimetableWithColor = friendlyTimetable.map( (entry: FriendlyTimetableEntry, index: number) => ({ @@ -62,21 +65,32 @@ export default function TimetableWeek({ const router = useRouter() const navigation = useNavigation() - + const stripTime = (date: Date): Date => { + return new Date(date.getFullYear(), date.getMonth(), date.getDate()) + } useLayoutEffect(() => { navigation.setOptions({ headerRight: () => ( { + const strippedLocalSelectedDate = + stripTime(localSelectedDate) + const strippedToday = stripTime(today) + + const targetDate = + strippedLocalSelectedDate.getTime() === + strippedToday.getTime() + ? firstElementeDate ?? today + : today // @ts-expect-error typescript doesn't know that goToDate exists - weekViewRef.current?.goToDate(today) - setLocalSelectedDate(today) + weekViewRef.current?.goToDate(new Date(targetDate)) + setLocalSelectedDate(new Date(targetDate)) }} /> ), headerLeft: () => , }) - }, [navigation]) + }, [navigation, localSelectedDate]) const isDark = theme.dark const isIOS = Platform.OS === 'ios' @@ -156,7 +170,6 @@ export default function TimetableWeek({ end={[1, 0.8]} style={{ ...styles.eventLine, - // backgroundColor: colors.primary, }} /> @@ -280,9 +293,6 @@ export default function TimetableWeek({ const weekViewRef = React.useRef(null) const isMountedRef = React.useRef(false) - const [localSelectedDate, setLocalSelectedDate] = - React.useState(selectedDate) - useEffect(() => { if (weekViewRef.current != null) { if ( diff --git a/src/components/Elements/Universal/BottomSheetRootBackground.tsx b/src/components/Elements/Universal/BottomSheetRootBackground.tsx index 765155bb..7ff0690e 100644 --- a/src/components/Elements/Universal/BottomSheetRootBackground.tsx +++ b/src/components/Elements/Universal/BottomSheetRootBackground.tsx @@ -5,8 +5,8 @@ import { Platform, StyleSheet, View } from 'react-native' const BottomSheetRootBackground = (): JSX.Element => { const { colors, dark } = useTheme() - const darkIos = 'rgba(0, 0, 0, 0.7)' - const lightIos = 'rgba(255, 255, 255, 0.346)' + const darkIos = 'rgba(0, 0, 0, 0.45)' + const lightIos = 'rgba(255, 255, 255, 0.5)' return Platform.OS === 'ios' ? ( { ]} > diff --git a/src/components/Elements/Universal/FormList.tsx b/src/components/Elements/Universal/FormList.tsx index b37d2e8a..e5bd29d7 100644 --- a/src/components/Elements/Universal/FormList.tsx +++ b/src/components/Elements/Universal/FormList.tsx @@ -118,6 +118,9 @@ const FormList: React.FC = ({ sections, rowStyle }) => { android={{ name: item.icon.android, size: 18, + variant: + item.icon + .androidVariant, }} /> )} diff --git a/src/components/Elements/Universal/LoginForm.tsx b/src/components/Elements/Universal/LoginForm.tsx index 2a2767ae..117432a0 100644 --- a/src/components/Elements/Universal/LoginForm.tsx +++ b/src/components/Elements/Universal/LoginForm.tsx @@ -1,13 +1,13 @@ import { createGuestSession, createSession } from '@/api/thi-session-handler' import { type Colors } from '@/components/colors' import { DashboardContext, UserKindContext } from '@/components/contexts' -import { trimErrorMsg } from '@/utils/api-utils' import { STATUS_URL, USER_EMPLOYEE, USER_GUEST, USER_STUDENT, -} from '@/utils/app-utils' +} from '@/data/constants' +import { trimErrorMsg } from '@/utils/api-utils' import { getContrastColor } from '@/utils/ui-utils' import { useTheme } from '@react-navigation/native' import Color from 'color' diff --git a/src/components/Elements/Universal/SectionsView.tsx b/src/components/Elements/Universal/SectionsView.tsx index b888ad30..452b4b09 100644 --- a/src/components/Elements/Universal/SectionsView.tsx +++ b/src/components/Elements/Universal/SectionsView.tsx @@ -56,7 +56,7 @@ const SectionView = ({ const styles = StyleSheet.create({ footerText: { marginTop: 6, - fontSize: 12, + fontSize: 12.5, paddingHorizontal: PAGE_PADDING, }, labelText: { diff --git a/src/components/Elements/Universal/WorkaroundStack.tsx b/src/components/Elements/Universal/WorkaroundStack.tsx index cc9dad68..15951d4f 100644 --- a/src/components/Elements/Universal/WorkaroundStack.tsx +++ b/src/components/Elements/Universal/WorkaroundStack.tsx @@ -1,5 +1,6 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack' import { type HeaderButtonProps } from '@react-navigation/native-stack/lib/typescript/src/types' +import { createStackNavigator } from '@react-navigation/stack' import React, { type ReactNode } from 'react' import { useTranslation } from 'react-i18next' import { Platform } from 'react-native' @@ -13,6 +14,7 @@ export interface WorkaroundStackProps { headerSearchBarOptions?: any headerRightElement?: ((props: HeaderButtonProps) => ReactNode) | undefined params?: any + androidFallback?: boolean } /* @@ -37,9 +39,30 @@ function WorkaroundStack({ headerRightElement = undefined, headerSearchBarOptions = undefined, params = {}, + androidFallback = false, }: WorkaroundStackProps): JSX.Element { const { t } = useTranslation('navigation') const Stack = createNativeStackNavigator() + const StackAndroid = createStackNavigator() + + // When using the native stack on Android, the header button is invisible. This is another workaround in the workaround. + if (Platform.OS === 'android' && androidFallback) { + return ( + + + + ) + } return ( , }, - { - key: 'news', - removable: true, - default: [USER_STUDENT, USER_EMPLOYEE], - card: () => , - }, - { - key: 'food', - removable: true, - default: [USER_STUDENT, USER_EMPLOYEE, USER_GUEST], - card: () => , - }, + { key: 'calendar', removable: true, @@ -44,7 +33,12 @@ export const AllCards: Card[] = [ default: [USER_STUDENT, USER_EMPLOYEE, USER_GUEST], card: () => , }, - + { + key: 'food', + removable: true, + default: [USER_STUDENT, USER_EMPLOYEE, USER_GUEST], + card: () => , + }, { key: 'library', removable: true, @@ -57,6 +51,12 @@ export const AllCards: Card[] = [ default: [USER_STUDENT, USER_EMPLOYEE, USER_GUEST], card: () => , }, + { + key: 'news', + removable: true, + default: [USER_STUDENT, USER_EMPLOYEE], + card: () => , + }, { key: 'lecturers', removable: true, diff --git a/src/contexts/dashboard.ts b/src/contexts/dashboard.ts index 1dd66c49..eed7ac16 100644 --- a/src/contexts/dashboard.ts +++ b/src/contexts/dashboard.ts @@ -1,5 +1,5 @@ import { AllCards, type Card } from '@/components/allCards' -import { USER_GUEST } from '@/utils/app-utils' +import { USER_GUEST } from '@/data/constants' import { useCallback, useMemo } from 'react' import { useMMKVObject } from 'react-native-mmkv' @@ -9,7 +9,6 @@ export function getDefaultDashboardOrder(userKind: string | undefined): { shown: string[] hidden: string[] } { - console.log('getDefaultDashboardOrder') const filter = (x: Card): boolean => x.default.includes(userKind ?? 'guest') return { shown: AllCards.filter(filter).map((card) => card.key), diff --git a/src/contexts/flow.ts b/src/contexts/flow.ts index a1e7b2a9..5341f78a 100644 --- a/src/contexts/flow.ts +++ b/src/contexts/flow.ts @@ -1,9 +1,8 @@ import { convertToMajorMinorPatch } from '@/utils/app-utils' +import * as Application from 'expo-application' import { useState } from 'react' import { useMMKVBoolean } from 'react-native-mmkv' -import packageInfo from '../../package.json' - export interface FlowHook { isOnboarded: boolean | undefined setOnboarded: (value: boolean) => void @@ -23,9 +22,10 @@ export interface FlowHook { * @returns An object containing the flow state and functions to update it. */ export function useFlow(): FlowHook { + const version = Application.nativeApplicationVersion ?? '0.0.0' const [isOnboarded, setOnboarded] = useMMKVBoolean('isOnboardedv1') const [isUpdated, setUpdated] = useMMKVBoolean( - `isUpdated-${convertToMajorMinorPatch(packageInfo.version)}` + `isUpdated-${convertToMajorMinorPatch(version)}` ) const [analyticsAllowed, setAnalyticsAllowed] = useMMKVBoolean('analytics') const [analyticsInitialized, setAnalyticsInitialized] = diff --git a/src/contexts/preferences.ts b/src/contexts/preferences.ts index d5c6ae2d..697a38aa 100644 --- a/src/contexts/preferences.ts +++ b/src/contexts/preferences.ts @@ -1,5 +1,5 @@ import { type CalendarMode } from '@/app/(tabs)/(timetable)/timetable' -import { defaultQuicklinks } from '@/utils/app-utils' +import { defaultQuicklinks } from '@/data/constants' import { useCallback, useMemo, useState } from 'react' import { useMMKVObject, useMMKVString } from 'react-native-mmkv' diff --git a/src/contexts/userKind.ts b/src/contexts/userKind.ts index 30dea6ac..d5803b8a 100644 --- a/src/contexts/userKind.ts +++ b/src/contexts/userKind.ts @@ -1,8 +1,8 @@ +import { USER_EMPLOYEE, USER_GUEST, USER_STUDENT } from '@/data/constants' import { extractFacultyFromPersonalData, getPersonalData, } from '@/utils/api-utils' -import { USER_EMPLOYEE, USER_GUEST, USER_STUDENT } from '@/utils/app-utils' import * as SecureStore from 'expo-secure-store' import { useCallback, useEffect, useMemo } from 'react' import { useMMKVString } from 'react-native-mmkv' diff --git a/src/data/changelog.json b/src/data/changelog.json index fc004073..8ca2ef5f 100644 --- a/src/data/changelog.json +++ b/src/data/changelog.json @@ -1,242 +1,6 @@ { "_note": "x.y = major.minor version. z versions are bugfixes and may not be listed here.", "version": { - "0.2": [ - { - "title": { - "de": "Neues Dashboard", - "en": "New Dashboard" - }, - "description": { - "de": "Hinzugefügt: Anpassbares Dashboard. Gehe zur Einstellungsseite, um es anzupassen.", - "en": "Added a customizable dashboard. Go to the settings page to customize it." - }, - "icon": { - "ios": "rectangle.3.group", - "android": "dashboard_customize" - } - }, - { - "title": { - "de": "Akzentfarben", - "en": "Accent Colors" - }, - "description": { - "de": "Wähle aus 9 verschiedenen Akzentfarben, um deine Erfahrung zu personalisieren.", - "en": "Choose from 9 different accent colors to personalize your experience." - }, - "icon": { - "ios": "paintpalette", - "android": "palette" - } - }, - { - "title": { - "de": "Onboarding & Changelog", - "en": "Onboarding & Changelog" - }, - "description": { - "de": "Diese neue Übersicht zeigt die wichtigsten Änderungen nach einem Update. Neue Nutzern wird außerdem ein Onboarding angezeigt.", - "en": "This new overview shows the most important changes after an update. New users also see an onboarding." - }, - "icon": { - "ios": "newspaper", - "android": "newspaper" - } - } - ], - "0.3": [ - { - "title": { - "de": "Lokalisierung (DE / EN)", - "en": "Localization (DE / EN)" - }, - "description": { - "de": "Die App ist nun in zwei Sprachen verfügbar. Die Sprache kann in den Einstellungen geändert werden.", - "en": "The app is now available in two languages. The language can be changed in the settings." - }, - "icon": { - "ios": "globe", - "android": "language" - } - }, - { - "title": { - "de": "Essens Variationen", - "en": "Food Variations" - }, - "description": { - "de": "Verfügbare Variationen werden nun in den Detaisl angezeigt. Außerdem kann nach dauerhaft verfügbaren Gerichten gefiltert werden.", - "en": "Available variants are now displayed in the details. In addition, you can filter by permanently available dishes." - }, - "icon": { - "ios": "carrot", - "android": "restaurant" - } - } - ], - "0.4": [ - { - "title": { - "de": "Studenplan", - "en": "Timetable" - }, - "description": { - "de": "Der Studenplan wurde von Grund auf überarbeitet. Du siehst nun alle nötigen Informationen auf einen Blick und kannst bei Bedarf mehr Details anzeigen lassen.", - "en": "The study plan has been completely revised. You can now see all the necessary information at a glance and can display more details if needed." - }, - "icon": { - "ios": "calendar", - "android": "today" - } - }, - { - "title": { - "de": "Notenübersicht", - "en": "Grades overview" - }, - "description": { - "de": "Nun kannst du deine Notenübersicht einsehen. Diese findest du in deinem Profil.", - "en": "You can now view your grades overview. These can be found in your profile." - }, - "icon": { - "ios": "book", - "android": "book" - } - } - ], - "0.5": [ - { - "title": { - "de": "Bibliothek", - "en": "Library" - }, - "description": { - "de": "Reserviere Plätze in der Bibliothek und nutze die digitale Bibliothekskarte, um Medien auszuleihen.", - "en": "Reserve seats in the library and use the digital library card to borrow media." - }, - "icon": { - "ios": "books.vertical", - "android": "book" - } - }, - { - "title": { - "de": "THI-News", - "en": "THI-News" - }, - "description": { - "de": "Rufe die neusten Pressemitteilungen der THI direkt in der App ab.", - "en": "Get the latest press releases from THI directly in the app." - }, - "icon": { - "ios": "newspaper", - "android": "newspaper" - } - }, - { - "title": { - "de": "Neue Icons und Farben", - "en": "New Icons and Colors" - }, - "description": { - "de": "Die App verwendet nun native Icons. Außerdem wurden die Akzentfarben komplett überarbeitet.", - "en": "The app uses native icons now. In addition, the accent colors have been revised completely." - }, - "icon": { - "ios": "paintpalette", - "android": "palette" - } - } - ], - "0.6": [ - { - "title": { - "de": "Kalender Neugestaltung", - "en": "Calendar Redesign" - }, - "description": { - "de": "Der Kalender verfügt nun über eine neue Agenda und Tagesansicht. Außerdem können nun Benachrichtigungen für Vorlesungen aktiviert werden.", - "en": "The calendar now has a new agenda and day view. In addition, notifications for lectures can now be activated." - }, - "icon": { - "ios": "calendar", - "android": "calendar_month" - } - }, - { - "title": { - "de": "Mensa Verbesserungen", - "en": "Food Improvements" - }, - "description": { - "de": "Swipen, Varianten, bessere Allergenanzeige und mehr. Der Essen Tab wurde komplett überarbeitet.", - "en": "Swiping, variants, better allergen display and more. The food tab has been completely revised." - }, - "icon": { - "ios": "fork.knife", - "android": "restaurant" - } - }, - { - "title": { - "de": "Anonyme Statistiken", - "en": "Anonymous Analytics" - }, - "description": { - "de": "Die App sammelt nun anonyme Nutzungsdaten, um die App zu verbessern. Deine Privatsphäre bleibt dabei zu jeder Zeit geschützt.", - "en": "The app now collects anonymous usage data to improve the app. Your privacy is protected at all times." - }, - "icon": { - "ios": "lock.shield", - "android": "query_stats" - } - } - ], - "0.7": [ - { - "title": { - "de": "Karten Redesign", - "en": "Map Redesign" - }, - "description": { - "de": "Die Karte wurde komplett überarbeitet und bietet bessere Bedienbarkeit, mehr Informationen und neues Design.", - "en": "The map has been completely redesigned and offers better usability, more information and a new design." - }, - "icon": { - "ios": "map", - "android": "map" - } - }, - { - "title": { - "de": "KI-gestützte Suche", - "en": "AI-powered Search" - }, - "description": { - "de": "Finde Räume schneller mit KI-gestützten Vorschlägen in der neuen Karten Suche.", - "en": "Discover rooms quicker with AI-powered suggestions in the new map search." - }, - "icon": { - "ios": "sparkle.magnifyingglass", - "android": "search" - } - }, - { - "title": { - "de": "Fehlerbehebungen & Performance", - "en": "Bug Fixes & Performance" - }, - "description": { - "de": "Viele kleine Details wurden optimiert, um die App noch besser und schneller zu machen.", - "en": "Many small details have been optimized to make the app even better and faster." - }, - "icon": { - "ios": "hammer", - "android": "bug_report" - } - } - ], "0.8": [ { "title": { diff --git a/src/data/constants.ts b/src/data/constants.ts new file mode 100644 index 00000000..16f19224 --- /dev/null +++ b/src/data/constants.ts @@ -0,0 +1,75 @@ +export const PRIVACY_URL = + 'https://assets.neuland.app/datenschutzerklaerung-app-v2.htm' +export const IMPRINT_URL = 'https://assets.neuland.app/impressum-app.htm' +export const STATUS_URL = 'https://status.neuland.app/status/app' +export const USER_STUDENT = 'student' +export const USER_EMPLOYEE = 'employee' +export const USER_GUEST = 'guest' +const primussLink = 'https://www3.primuss.de/cgi-bin/login/index.pl?FH=fhin' +const moodleLink = 'https://moodle.thi.de/' +const mailLink = 'https://outlook.thi.de/' +const studverLink = 'https://studverthi.de' +const marketplaceLink = 'https://www.thi.de/service/marketplace/' +const myThiLink = 'https://mythi.de' +const libraryLink = 'https://opac.ku.de/index-hi.html' + +export const quicklinks = [ + { + key: 'primuss', + url: primussLink, + icon: { + ios: 'graduationcap', + android: 'school', + }, + }, + { + key: 'moodle', + url: moodleLink, + icon: { + ios: 'tray.full', + android: 'note_stack', + }, + }, + { + key: 'webmail', + url: mailLink, + icon: { + ios: 'envelope', + android: 'mail', + }, + }, + { + key: 'studVer', + url: studverLink, + icon: { + ios: 'person.bubble', + android: 'podium', + }, + }, + { + key: 'marketplace', + url: marketplaceLink, + icon: { + ios: 'cart', + android: 'shopping_cart', + }, + }, + { + key: 'library', + url: libraryLink, + icon: { + ios: 'text.book.closed', + android: 'book_4', + }, + }, + { + key: 'myTHI', + url: myThiLink, + icon: { + ios: 'circle.grid.2x2', + android: 'badge', + }, + }, +] + +export const defaultQuicklinks = ['primuss', 'moodle', 'webmail'] diff --git a/src/data/licenses.json b/src/data/licenses.json index e4b909c9..853f0cb4 100644 --- a/src/data/licenses.json +++ b/src/data/licenses.json @@ -53,6 +53,12 @@ "licenseUrl": "https://github.com/react-native-netinfo/react-native-netinfo/raw/master/LICENSE", "parents": "neuland" }, + "@react-navigation/stack@6.4.1": { + "licenses": "MIT", + "repository": "https://github.com/react-navigation/react-navigation", + "licenseUrl": "https://github.com/react-navigation/react-navigation/raw/master/LICENSE", + "parents": "neuland" + }, "@shopify/flash-list@1.6.4": { "licenses": "MIT", "repository": "https://github.com/Shopify/flash-list", @@ -65,13 +71,13 @@ "licenseUrl": "https://github.com/TanStack/query/raw/master/LICENSE", "parents": "neuland" }, - "@tanstack/react-query-persist-client@5.51.21": { + "@tanstack/react-query-persist-client@5.51.23": { "licenses": "MIT", "repository": "https://github.com/TanStack/query", "licenseUrl": "https://github.com/TanStack/query/raw/master/LICENSE", "parents": "neuland" }, - "@tanstack/react-query@5.51.21": { + "@tanstack/react-query@5.51.23": { "licenses": "MIT", "repository": "https://github.com/TanStack/query", "licenseUrl": "https://github.com/TanStack/query/raw/master/LICENSE", @@ -215,7 +221,7 @@ "licenseUrl": "https://github.com/i18next/i18next/raw/master/LICENSE", "parents": "neuland" }, - "metro@0.80.9": { + "metro@0.80.10": { "licenses": "MIT", "repository": "https://github.com/facebook/metro", "licenseUrl": "https://github.com/facebook/metro", diff --git a/src/localization/de/ios.json b/src/localization/de/ios.json new file mode 100644 index 00000000..e33f28ea --- /dev/null +++ b/src/localization/de/ios.json @@ -0,0 +1,5 @@ +{ + "CFBundleDisplayName": "Neuland Next", + "NSFaceIDUsageDescription": "Erlaube Neuland Next die Verwendung von Face ID.", + "NSLocationWhenInUseUsageDescription": "Erlaube Neuland Next den Zugriff auf deinen Standort, um deine Position auf der Karte anzuzeigen." +} diff --git a/src/localization/de/navigation.json b/src/localization/de/navigation.json index 06dfe134..bb2e4238 100644 --- a/src/localization/de/navigation.json +++ b/src/localization/de/navigation.json @@ -13,6 +13,7 @@ "allergens": "Allergene", "details": "Details", "theme": "Design", + "accent": "Akzentfarbe", "profile": "Profil", "about": "Über", "advancedSearch": "Erweiterte Suche", diff --git a/src/localization/de/settings.json b/src/localization/de/settings.json index 3bf751f1..70a6d78e 100644 --- a/src/localization/de/settings.json +++ b/src/localization/de/settings.json @@ -24,6 +24,7 @@ }, "appearance": { "title": "Darstellung", + "accent": "Akzentfarbe", "theme": "Design" }, "language": { @@ -131,7 +132,8 @@ "title": "Design", "default": "Automatisch", "dark": "Dunkel", - "light": "Hell" + "light": "Hell", + "footer": "Ändere das Farbschema der App. Dies ermöglicht es dir, die Systemeinstellungen zu überschreiben." } }, "dashboard": { diff --git a/src/localization/en/common.json b/src/localization/en/common.json index c1a3a9d0..7c5d4c86 100644 --- a/src/localization/en/common.json +++ b/src/localization/en/common.json @@ -61,7 +61,7 @@ "quicklinks": { "primuss": "Primuss", "moodle": "Moodle", - "webmail": "Outlook", + "webmail": "E-Mail", "studVer": "StudVer", "marketplace": "Market-place", "myTHI": "MyTHI", diff --git a/src/localization/en/ios.json b/src/localization/en/ios.json new file mode 100644 index 00000000..44831771 --- /dev/null +++ b/src/localization/en/ios.json @@ -0,0 +1,5 @@ +{ + "CFBundleDisplayName": "Neuland Next", + "NSFaceIDUsageDescription": "Allow Neuland Next to use Face ID.", + "NSLocationWhenInUseUsageDescription": "Allow Neuland Next to access your location to show your location on the map." +} diff --git a/src/localization/en/navigation.json b/src/localization/en/navigation.json index 598c43b7..8b433b19 100644 --- a/src/localization/en/navigation.json +++ b/src/localization/en/navigation.json @@ -12,6 +12,7 @@ "allergens": "Allergens", "details": "Details", "theme": "Design", + "accent": "Accent Color", "profile": "Profile", "about": "About", "advancedSearch": "Advanced Search", diff --git a/src/localization/en/settings.json b/src/localization/en/settings.json index 65ff3ea8..20bf3cda 100644 --- a/src/localization/en/settings.json +++ b/src/localization/en/settings.json @@ -24,8 +24,10 @@ }, "appearance": { "title": "Appearance", + "accent": "Accent Color", "theme": "Design" }, + "language": { "title": "Change language", "message": "Confirm to change the app language to German.", @@ -132,7 +134,8 @@ "title": "Theme", "default": "Automatic", "dark": "Dark", - "light": "Light" + "light": "Light", + "footer": "Change color scheme of the app. This allows you to override the system settings." } }, "dashboard": { diff --git a/src/types/components.ts b/src/types/components.ts index 4e2ec69f..2340b152 100644 --- a/src/types/components.ts +++ b/src/types/components.ts @@ -10,6 +10,7 @@ export interface SectionGroup { ios: string android: MaterialIcon | CommunityIcon iosFallback?: boolean + androidVariant?: 'outlined' | 'filled' } disabled?: boolean onPress?: () => Promise | void diff --git a/src/utils/api-utils.ts b/src/utils/api-utils.ts index 0677c1e8..2888e4aa 100644 --- a/src/utils/api-utils.ts +++ b/src/utils/api-utils.ts @@ -1,5 +1,6 @@ import API from '@/api/authenticated-api' import { createGuestSession } from '@/api/thi-session-handler' +import { USER_GUEST } from '@/data/constants' import courseShortNames from '@/data/course-short-names.json' import { type CourseShortNames } from '@/types/data' import { type PersDataDetails } from '@/types/thi-api' @@ -7,8 +8,6 @@ import { type QueryClient } from '@tanstack/react-query' import { router } from 'expo-router' import { getItemAsync } from 'expo-secure-store' -import { USER_GUEST } from './app-utils' - export const networkError = 'Network request failed' export const guestError = 'User is logged in as guest' export const permissionError = '"Service for user-group not defined" (-120)' diff --git a/src/utils/app-utils.ts b/src/utils/app-utils.ts index 4f7a8f6e..fe58390b 100644 --- a/src/utils/app-utils.ts +++ b/src/utils/app-utils.ts @@ -29,79 +29,3 @@ export function arraysEqual(arr1: any[], arr2: any[]): boolean { } return true } - -export const PRIVACY_URL = - 'https://assets.neuland.app/datenschutzerklaerung-app-v2.htm' -export const IMPRINT_URL = 'https://assets.neuland.app/impressum-app.htm' -export const STATUS_URL = 'https://status.neuland.app/status/app' -export const USER_STUDENT = 'student' -export const USER_EMPLOYEE = 'employee' -export const USER_GUEST = 'guest' -const primussLink = 'https://www3.primuss.de/cgi-bin/login/index.pl?FH=fhin' -const moodleLink = 'https://moodle.thi.de/' -const mailLink = 'https://outlook.thi.de/' -const studverLink = 'https://studverthi.de' -const marketplaceLink = 'https://www.thi.de/service/marketplace/' -const myThiLink = 'https://mythi.de' -const libraryLink = 'https://opac.ku.de/index-hi.html' - -export const quicklinks = [ - { - key: 'primuss', - url: primussLink, - icon: { - ios: 'graduationcap', - android: 'link', - }, - }, - { - key: 'moodle', - url: moodleLink, - icon: { - ios: 'tray.full', - android: 'link', - }, - }, - { - key: 'webmail', - url: mailLink, - icon: { - ios: 'envelope', - android: 'link', - }, - }, - { - key: 'studVer', - url: studverLink, - icon: { - ios: 'person.bubble', - android: 'link', - }, - }, - { - key: 'marketplace', - url: marketplaceLink, - icon: { - ios: 'cart', - android: 'link', - }, - }, - { - key: 'myTHI', - url: myThiLink, - icon: { - ios: 'circle.grid.2x2', - android: 'link', - }, - }, - { - key: 'library', - url: libraryLink, - icon: { - ios: 'text.book.closed', - android: 'link', - }, - }, -] - -export const defaultQuicklinks = ['primuss', 'moodle', 'webmail'] diff --git a/src/utils/food-utils.ts b/src/utils/food-utils.ts index 42832f6e..cd1a542e 100644 --- a/src/utils/food-utils.ts +++ b/src/utils/food-utils.ts @@ -1,13 +1,13 @@ import NeulandAPI from '@/api/neuland-api' import { type FoodLanguage } from '@/contexts/foodFilter' import allergenMap from '@/data/allergens.json' +import { USER_EMPLOYEE, USER_GUEST, USER_STUDENT } from '@/data/constants' import flapMap from '@/data/mensa-flags.json' import { type LanguageKey } from '@/localization/i18n' import { type Food, type Meal, type Name } from '@/types/neuland-api' import { type Labels, type Prices } from '@/types/utils' import { type TFunction } from 'i18next' -import { USER_EMPLOYEE, USER_GUEST, USER_STUDENT } from './app-utils' import { formatISODate } from './date-utils' /**