diff --git a/android/app/build.gradle b/android/app/build.gradle
index 8985cc10..b4fe0a56 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -91,7 +91,7 @@ android {
applicationId 'app.neuland'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 230
+ versionCode 231
versionName "0.11.0"
}
signingConfigs {
diff --git a/app.config.json b/app.config.json
index f31fe56a..4e2ad10d 100644
--- a/app.config.json
+++ b/app.config.json
@@ -36,7 +36,7 @@
"android": {
"package": "app.neuland",
"userInterfaceStyle": "automatic",
- "versionCode": 230
+ "versionCode": 231
},
"sdkVersion": "52.0.0",
"experiments": {
diff --git a/bun.lockb b/bun.lockb
index c4cfcbc6..cb7c1603 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 3c90277d..a25d19a0 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -233,6 +233,8 @@ PODS:
- ExpoModulesCore
- ExpoClipboard (7.0.0):
- ExpoModulesCore
+ - ExpoCrypto (14.0.1):
+ - ExpoModulesCore
- ExpoFileSystem (18.0.4):
- ExpoModulesCore
- ExpoFont (13.0.1):
@@ -2295,6 +2297,7 @@ DEPENDENCIES:
- ExpoBlur (from `../node_modules/expo-blur/ios`)
- ExpoBrightness (from `../node_modules/expo-brightness/ios`)
- ExpoClipboard (from `../node_modules/expo-clipboard/ios`)
+ - ExpoCrypto (from `../node_modules/expo-crypto/ios`)
- ExpoFileSystem (from `../node_modules/expo-file-system/ios`)
- ExpoFont (from `../node_modules/expo-font/ios`)
- ExpoHaptics (from `../node_modules/expo-haptics/ios`)
@@ -2438,6 +2441,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-brightness/ios"
ExpoClipboard:
:path: "../node_modules/expo-clipboard/ios"
+ ExpoCrypto:
+ :path: "../node_modules/expo-crypto/ios"
ExpoFileSystem:
:path: "../node_modules/expo-file-system/ios"
ExpoFont:
@@ -2651,6 +2656,7 @@ SPEC CHECKSUMS:
ExpoBlur: 562b3da84d3cd79016c411671eaf71a404266415
ExpoBrightness: ce777f6a365592f745428cb0acc9066b400182e8
ExpoClipboard: 166ca8c13ca1041f5ba619a211f59427ab5da8a7
+ ExpoCrypto: 8465f71eb3798c194c0a509d4a76e5c429656d83
ExpoFileSystem: dc2679a2b5d4c465ca881129074da95faee943d5
ExpoFont: 7522d869d84ee2ee8093ee997fef5b86f85d856b
ExpoHaptics: e636188d1d5f7ccb79f3c1bfab47aaf5a1768c73
diff --git a/package.json b/package.json
index 3429586d..4d68e291 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
"expo-build-properties": "~0.13.1",
"expo-clipboard": "~7.0.0",
"expo-constants": "~17.0.3",
+ "expo-crypto": "~14.0.1",
"expo-haptics": "~14.0.0",
"expo-linear-gradient": "~14.0.1",
"expo-local-authentication": "~15.0.1",
@@ -80,6 +81,7 @@
"react-native-pager-view": "6.5.1",
"react-native-paper": "5.12.5",
"react-native-reanimated": "~3.16.3",
+ "react-native-reanimated-carousel": "^3.5.1",
"react-native-safe-area-context": "4.12.0",
"react-native-screens": "~4.1.0",
"react-native-select-dropdown": "^4.0.1",
diff --git a/src/components/Cards/NewsCard.tsx b/src/components/Cards/NewsCard.tsx
new file mode 100644
index 00000000..a3329a04
--- /dev/null
+++ b/src/components/Cards/NewsCard.tsx
@@ -0,0 +1,129 @@
+import API from '@/api/authenticated-api'
+import { UserKindContext } from '@/components/contexts'
+import { USER_GUEST } from '@/data/constants'
+import { type ThiNews } from '@/types/thi-api'
+import { useQuery } from '@tanstack/react-query'
+import React, { useContext, useLayoutEffect, useRef } from 'react'
+import { useTranslation } from 'react-i18next'
+import { Dimensions, Image, Linking, Pressable, Text, View } from 'react-native'
+import Carousel from 'react-native-reanimated-carousel'
+import { createStyleSheet, useStyles } from 'react-native-unistyles'
+
+import BaseCard from './BaseCard'
+
+const NewsCard: React.FC = () => {
+ const ref = useRef(null)
+ const { t } = useTranslation('navigation')
+ const { styles } = useStyles(stylesheet)
+ const { userKind = USER_GUEST } = useContext(UserKindContext)
+ const { data } = useQuery({
+ queryKey: ['thiNews'],
+ queryFn: async () => await API.getThiNews(),
+ staleTime: 1000 * 60 * 10, // 10 minutes
+ gcTime: 1000 * 60 * 60 * 24, // 24 hours
+ enabled: userKind !== USER_GUEST,
+ })
+ const [cardWidth, setCardWidth] = React.useState(
+ Dimensions.get('window').width - 32
+ )
+ useLayoutEffect(() => {
+ if (ref.current != null) {
+ // @ts-expect-error unstable_getBoundingClientRect is not in the types
+ const width = ref.current.unstable_getBoundingClientRect()
+ .width as number
+ setCardWidth(width)
+ }
+ }, [])
+
+ const renderEvent = (event: ThiNews): JSX.Element => {
+ return (
+ {
+ void Linking.openURL(event.href)
+ }}
+ >
+
+
+ {event.title}
+
+
+ )
+ }
+
+ return (
+
+
+ {data != null && data.length > 0 && (
+ (
+
+ {renderEvent(data[index])}
+
+ )}
+ >
+ )}
+ {data != null && data.length > 2 && (
+
+
+ {t('cards.news.more', {
+ count: data.length,
+ })}
+
+
+ )}
+
+
+ )
+}
+
+const stylesheet = createStyleSheet((theme) => ({
+ cardsFilled: { gap: 8, paddingTop: 12 },
+ carousel: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginTop: 4,
+ width: '100%',
+ },
+ description: {
+ color: theme.colors.labelColor,
+ fontSize: 14,
+ fontWeight: '500',
+ },
+ eventContainer: {
+ alignItems: 'center',
+ backgroundColor: theme.colors.cardButton,
+ borderRadius: theme.radius.md,
+ flexDirection: 'row',
+ gap: 12,
+ marginHorizontal: 4,
+ padding: 10,
+ },
+ eventTitle: {
+ color: theme.colors.text,
+ flexShrink: 1,
+ fontSize: 15,
+ fontWeight: '500',
+ },
+ imageContainer: {
+ borderRadius: theme.radius.sm,
+ height: 80,
+ resizeMode: 'cover',
+ width: '35%',
+ },
+}))
+
+export default NewsCard
diff --git a/src/components/Map/MapScreen.tsx b/src/components/Map/MapScreen.tsx
index 1378b932..e2cc41c8 100644
--- a/src/components/Map/MapScreen.tsx
+++ b/src/components/Map/MapScreen.tsx
@@ -888,7 +888,11 @@ const MapScreen = (): JSX.Element => {
}}
style={layerStyles.osmBackground}
>
-
+
{'© OpenStreetMap'}
diff --git a/src/components/allCards.tsx b/src/components/allCards.tsx
index 07453e7d..84334d79 100644
--- a/src/components/allCards.tsx
+++ b/src/components/allCards.tsx
@@ -11,6 +11,7 @@ import {
TimetableCard,
} from './Cards'
import LibraryCard from './Cards/LibraryCard'
+import NewsCard from './Cards/NewsCard'
export const AllCards: Card[] = [
{
@@ -62,7 +63,7 @@ export const AllCards: Card[] = [
removable: true,
initial: [USER_STUDENT, USER_EMPLOYEE],
allowed: [USER_STUDENT, USER_EMPLOYEE],
- card: () => ,
+ card: () => ,
},
{
key: 'lecturers',
diff --git a/src/data/licenses.json b/src/data/licenses.json
index f498f081..ac9ed0c5 100644
--- a/src/data/licenses.json
+++ b/src/data/licenses.json
@@ -143,6 +143,12 @@
"licenseUrl": "https://github.com/expo/expo",
"parents": "neuland"
},
+ "expo-crypto@14.0.1": {
+ "licenses": "MIT",
+ "repository": "https://github.com/expo/expo",
+ "licenseUrl": "https://github.com/expo/expo",
+ "parents": "neuland"
+ },
"expo-haptics@14.0.0": {
"licenses": "MIT",
"repository": "https://github.com/expo/expo",
@@ -191,7 +197,7 @@
"licenseUrl": "https://github.com/expo/expo",
"parents": "neuland"
},
- "expo-splash-screen@0.29.13": {
+ "expo-splash-screen@0.29.16": {
"licenses": "MIT",
"repository": "https://github.com/expo/expo",
"licenseUrl": "https://github.com/expo/expo",
@@ -203,7 +209,7 @@
"licenseUrl": "https://github.com/expo/expo",
"parents": "neuland"
},
- "expo@52.0.15": {
+ "expo@52.0.17": {
"licenses": "MIT",
"repository": "https://github.com/expo/expo",
"licenseUrl": "https://github.com/expo/expo",
@@ -215,12 +221,6 @@
"licenseUrl": "https://github.com/krisk/Fuse/raw/master/LICENSE",
"parents": "neuland"
},
- "graphql-request@7.1.2": {
- "licenses": "MIT",
- "repository": "https://github.com/jasonkuhrt/graphql-request",
- "licenseUrl": "https://github.com/jasonkuhrt/graphql-request/raw/master/LICENSE",
- "parents": "neuland"
- },
"graphql@16.9.0": {
"licenses": "MIT",
"repository": "https://github.com/graphql/graphql-js",
@@ -329,6 +329,12 @@
"licenseUrl": "https://github.com/callstack/react-native-paper/raw/master/LICENSE.md",
"parents": "neuland"
},
+ "react-native-reanimated-carousel@3.5.1": {
+ "licenses": "MIT",
+ "repository": "https://github.com/dohooo/react-native-reanimated-carousel",
+ "licenseUrl": "https://github.com/dohooo/react-native-reanimated-carousel/raw/master/LICENSE",
+ "parents": "neuland"
+ },
"react-native-reanimated@3.16.3": {
"licenses": "MIT",
"repository": "https://github.com/software-mansion/react-native-reanimated",
diff --git a/src/localization/de/navigation.json b/src/localization/de/navigation.json
index f5c503e3..6c73ef26 100644
--- a/src/localization/de/navigation.json
+++ b/src/localization/de/navigation.json
@@ -62,6 +62,9 @@
"events": {
"by": "von {{name}}"
},
+ "news": {
+ "more": "Lese alle {{count}} Artikel"
+ },
"calendar": {
"exam": "Prüfung: {{name}}",
"ends": "endet "
diff --git a/src/localization/en/navigation.json b/src/localization/en/navigation.json
index e558c365..d6b8a92c 100644
--- a/src/localization/en/navigation.json
+++ b/src/localization/en/navigation.json
@@ -62,6 +62,9 @@
"events": {
"by": "by {{name}}"
},
+ "news": {
+ "more": "Read all {{count}} articles"
+ },
"calendar": {
"exam": "Exam: {{name}}",
"ends": "ends "