diff --git a/ios/NeulandNext.xcodeproj/project.pbxproj b/ios/NeulandNext.xcodeproj/project.pbxproj index 621ef711..b22e4628 100644 --- a/ios/NeulandNext.xcodeproj/project.pbxproj +++ b/ios/NeulandNext.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -14,6 +14,7 @@ 79A805BC94BC0AD1674AEE08 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 2DE9A45D19FFF21A2FC7974A /* PrivacyInfo.xcprivacy */; }; 96905EF65AED1B983A6B3ABC /* libPods-NeulandNext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-NeulandNext.a */; }; B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; }; + 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 */; }; /* End PBXBuildFile section */ @@ -26,7 +27,7 @@ 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 = ""; }; 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; name = PrivacyInfo.xcprivacy; path = NeulandNext/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 2DE9A45D19FFF21A2FC7974A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = NeulandNext/PrivacyInfo.xcprivacy; sourceTree = ""; }; 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 = ""; }; 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-NeulandNext.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NeulandNext.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 6C2E3173556A471DD304B334 /* 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 = ""; }; @@ -42,6 +43,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B7E3797A2C54A52E006A4193 /* MapLibre in Frameworks */, 96905EF65AED1B983A6B3ABC /* libPods-NeulandNext.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -361,18 +363,24 @@ baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-NeulandNext.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = NeulandNext/NeulandNext.entitlements; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = FSXB76X6V2; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "FB_SONARKIT_ENABLED=1", ); INFOPLIST_FILE = NeulandNext/Info.plist; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education"; IPHONEOS_DEPLOYMENT_TARGET = 13.4; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 0.8.3; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -394,14 +402,20 @@ baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-NeulandNext.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = NeulandNext/NeulandNext.entitlements; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = FSXB76X6V2; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = NeulandNext/Info.plist; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education"; IPHONEOS_DEPLOYMENT_TARGET = 13.4; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 0.8.3; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -469,14 +483,14 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.4; LD = ""; LDPLUSPLUS = ""; - LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); 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; @@ -528,13 +542,13 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.4; LD = ""; LDPLUSPLUS = ""; - LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; - LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = ( + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, "$(inherited)", - " ", ); + LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = "$(inherited) "; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/ios/NeulandNext/Info.plist b/ios/NeulandNext/Info.plist index 12031a62..9814777b 100644 --- a/ios/NeulandNext/Info.plist +++ b/ios/NeulandNext/Info.plist @@ -106,7 +106,7 @@ CFBundleVersion - 46 + 47 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS @@ -120,6 +120,8 @@ NSFaceIDUsageDescription Allow $(PRODUCT_NAME) to use Face ID. + NSLocationWhenInUseUsageDescription + Allow $(PRODUCT_NAME) to access your location. NSUserActivityTypes $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route diff --git a/ios/NeulandNext/NeulandNext.entitlements b/ios/NeulandNext/NeulandNext.entitlements index 4adf5e79..bddeabf7 100644 --- a/ios/NeulandNext/NeulandNext.entitlements +++ b/ios/NeulandNext/NeulandNext.entitlements @@ -1,11 +1,11 @@ - - com.apple.developer.associated-domains - - webcredentials:neuland.app - activitycontinuation:neuland.app - - - \ No newline at end of file + + com.apple.developer.associated-domains + + webcredentials:neuland.app + activitycontinuation:neuland.app + + + diff --git a/src/api/neuland-api.ts b/src/api/neuland-api.ts index e9fe0dde..ccfd3ecd 100644 --- a/src/api/neuland-api.ts +++ b/src/api/neuland-api.ts @@ -66,73 +66,58 @@ class NeulandAPIClient { return await this.performGraphQLQuery(gql` query { food(locations: [${locations.map((x) => `"${x}"`).join(',')}]) { - timestamp - meals { - variants { - name { - de - en - } - additional - prices { - student - employee - guest - } - id - allergens - flags - nutrition { - kj - kcal - fat - fatSaturated - carbs - sugar - fiber - protein - salt - } - originalLanguage - static - restaurant - parent { - id - category - } - - } - name { - de - en - } - id - category - prices { - student - employee - guest - } - allergens - flags - nutrition { - kj - kcal - fat - fatSaturated - carbs - sugar - fiber - protein - salt - } - originalLanguage - static - restaurant - } + foodData { + timestamp + meals { + name { + de + en + } + id + category + prices { + student + employee + guest + } + allergens + flags + nutrition { + kj + kcal + fat + fatSaturated + carbs + sugar + fiber + protein + salt + } + variants { + additional + id + allergens + flags + originalLanguage + static + restaurant + parent { + id + category + } + } + originalLanguage + static + restaurant + } + } + errors { + location + message + } + } } - } - `) + `) } /** diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index 953dfcb8..b46c8e9e 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -110,26 +110,6 @@ export default function HomeLayout(): JSX.Element { symbolName: 'fork.knife', iconName: 'silverware_fork_knife', }, - ...(Platform.OS === 'ios' - ? [ - { - id: 'appIcon', - type: 'appIcon', - title: 'App Icon', - subtitle: t( - // @ts-expect-error no types - `appIcon.names.${appIcon}`, - { - ns: 'settings', - } - ), - data: { - path: '(user)/appicon', - }, - symbolName: 'paintpalette', - }, - ] - : []), ] useEffect(() => { diff --git a/src/app/(user)/about.tsx b/src/app/(user)/about.tsx index 925a03bb..c3899d8b 100644 --- a/src/app/(user)/about.tsx +++ b/src/app/(user)/about.tsx @@ -5,7 +5,7 @@ import SingleSectionPicker from '@/components/Elements/Universal/SingleSectionPi import { type Colors } from '@/components/colors' import { AppIconContext, FlowContext } from '@/components/contexts' import { type FormListSections } from '@/types/components' -import { IMPRINT_URL, PRIVACY_URL } from '@/utils/app-utils' +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' @@ -67,7 +67,7 @@ export default function About(): JSX.Element { android: 'sync_problem', }, onPress: () => { - void Linking.openURL('https://status.neuland.app') + void Linking.openURL(STATUS_URL) }, }, ], diff --git a/src/app/(user)/login.tsx b/src/app/(user)/login.tsx index a57ec70b..60cf854d 100644 --- a/src/app/(user)/login.tsx +++ b/src/app/(user)/login.tsx @@ -1,6 +1,6 @@ import LoginForm from '@/components/Elements/Universal/LoginForm' import { type Colors } from '@/components/colors' -import { PRIVACY_URL } from '@/utils/app-utils' +import { PRIVACY_URL, STATUS_URL } from '@/utils/app-utils' import { useTheme } from '@react-navigation/native' import * as Haptics from 'expo-haptics' import { router, useLocalSearchParams } from 'expo-router' @@ -183,20 +183,35 @@ export default function Login(): JSX.Element { - { - void Linking.openURL(PRIVACY_URL) - }} - style={styles.privacyLink} - > - + { + void Linking.openURL(STATUS_URL) + }} + > + + {'System Status'} + + + { + void Linking.openURL(PRIVACY_URL) }} > - {t('onboarding.links.privacy')} - - + + {t('onboarding.links.privacy')} + + + @@ -228,11 +243,15 @@ const styles = StyleSheet.create({ fontWeight: '400', minHeight: 30, }, + linkContainer: { + bottom: 70, + position: 'absolute', + gap: 6, + alignSelf: 'center', + alignItems: 'center', + }, privacyLink: { textAlign: 'center', fontSize: 14, - alignSelf: 'center', - bottom: 70, - position: 'absolute', }, }) diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index 84244dae..4ea73825 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -62,392 +62,386 @@ function RootLayout(): JSX.Element { return ( <> - - + + + + + + + + + + + + ({ + title: 'App Icon', + animation: 'slide_from_right', + presentation: + route.params?.fromAppShortcut === 'true' + ? 'modal' + : undefined, + })} + /> + + + + + - - - - - - - - - - - - ({ - title: 'App Icon', - animation: 'slide_from_right', - presentation: - route.params?.fromAppShortcut === 'true' - ? 'modal' - : undefined, - })} - /> - - - - - - - - - - - - - - + + + + + + + + + ( - { - router.push('(pages)/libraryCode') + headerRight: () => ( + { + router.push('(pages)/libraryCode') + }} + accessibilityLabel={t('button.libraryBarcode', { + ns: 'accessibility', + })} + > + - - - ), - }} - /> - - - + + ), + }} + /> + + + + animation: 'none', + gestureEnabled: false, + ...Platform.select({ + ios: { + presentation: 'fullScreenModal', + }, + }), + }} + /> - - - + + ) } const ProviderComponent = (): JSX.Element => { return ( - - - + + + + + ) } diff --git a/src/components/Elements/Error/ActionButtons.tsx b/src/components/Elements/Error/ActionButtons.tsx index 4973854a..ddb906f0 100644 --- a/src/components/Elements/Error/ActionButtons.tsx +++ b/src/components/Elements/Error/ActionButtons.tsx @@ -1,4 +1,5 @@ import { type Colors } from '@/components/colors' +import { STATUS_URL } from '@/utils/app-utils' import { useTheme } from '@react-navigation/native' import * as Application from 'expo-application' import React from 'react' @@ -70,7 +71,7 @@ export const StatusButton = (): JSX.Element => { }, ]} onPress={() => { - void Linking.openURL('https://status.neuland.app') + void Linking.openURL(STATUS_URL) }} > diff --git a/src/components/Elements/Map/BottomSheetMap.tsx b/src/components/Elements/Map/BottomSheetMap.tsx index 6ee2cd9c..923b000e 100644 --- a/src/components/Elements/Map/BottomSheetMap.tsx +++ b/src/components/Elements/Map/BottomSheetMap.tsx @@ -186,7 +186,6 @@ const MapBottomSheet: React.FC = ({ addUnlockedAppIcon('retro') } }, [localSearch]) - console.log('history', searchHistory) const textInputRef = useRef(null) const [searchFocused, setSearchFocused] = React.useState(false) const searchbarBackground = isDark diff --git a/src/components/Elements/Universal/LoginForm.tsx b/src/components/Elements/Universal/LoginForm.tsx index 11a4f700..69e3e8ed 100644 --- a/src/components/Elements/Universal/LoginForm.tsx +++ b/src/components/Elements/Universal/LoginForm.tsx @@ -108,9 +108,25 @@ const LoginForm = ({ msg = t('login.alert.error.backend') } console.log('Login failed', notice, infoMsg) - Alert.alert(title, msg, [{ text: 'OK' }], { - cancelable: false, - }) + Alert.alert( + title, + msg, + [ + { text: 'OK' }, + { + text: 'System Status', + onPress: () => { + // Replace with your navigation logic + Linking.openURL( + 'https://your-system-status-page-url.com' + ) + }, + }, + ], + { + cancelable: false, + } + ) } } diff --git a/src/utils/animation-utils.ts b/src/utils/animation-utils.ts index d505d5cb..a5ab527a 100644 --- a/src/utils/animation-utils.ts +++ b/src/utils/animation-utils.ts @@ -64,7 +64,7 @@ export function withBouncing( return false } - const onBeginn = ( + const onStart = ( state: AnimationState, _value: number, now: number @@ -74,7 +74,7 @@ export function withBouncing( state.direction = 1 } - return { onFrame, onBeginn } + return { onFrame, onStart } } ) } diff --git a/src/utils/app-utils.ts b/src/utils/app-utils.ts index 841ae481..3fbf751a 100644 --- a/src/utils/app-utils.ts +++ b/src/utils/app-utils.ts @@ -33,3 +33,4 @@ export function arraysEqual(arr1: any[], arr2: any[]): boolean { export const PRIVACY_URL = 'https://assets.neuland.app/datenschutzerklaerung-app.htm' export const IMPRINT_URL = 'https://assets.neuland.app/impressum-app.htm' +export const STATUS_URL = 'https://status.neuland.app/status/app' diff --git a/src/utils/food-utils.ts b/src/utils/food-utils.ts index 63225ab5..b8a7aac5 100644 --- a/src/utils/food-utils.ts +++ b/src/utils/food-utils.ts @@ -18,7 +18,9 @@ export async function loadFoodEntries( restaurants: string[], includeStatic: boolean ): Promise { - const data = [(await NeulandAPI.getFoodPlan(restaurants)).food] as Food[][] + const data = [ + (await NeulandAPI.getFoodPlan(restaurants)).food.foodData, + ] as Food[][] // create day entries for next 7 days (current and next week including the weekend) starting from monday let days: Date[] = Array.from({ length: 7 }, (_, i) => {