Skip to content

Commit

Permalink
feat(calendar): adds calendar page and card
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert27 committed Aug 15, 2023
1 parent cb90a0d commit 25750dd
Show file tree
Hide file tree
Showing 46 changed files with 1,349 additions and 353 deletions.
2 changes: 1 addition & 1 deletion src/app/(food)/allergens.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SectionPicker } from '@/components/SectionPicker'
import { SectionPicker } from '@/components/Elements/Universal/SectionPicker'
import { type Colors } from '@/stores/colors'
import allergenMap from '@/stores/data/allergens.json'
import { FoodFilterContext } from '@/stores/provider'
Expand Down
2 changes: 1 addition & 1 deletion src/app/(food)/details.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import FormList from '@/components/FormList'
import FormList from '@/components/Elements/Universal/FormList'
import { type Colors } from '@/stores/colors'
import allergenMap from '@/stores/data/allergens.json'
import flapMap from '@/stores/data/mensa-flags.json'
Expand Down
2 changes: 1 addition & 1 deletion src/app/(food)/flags.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SectionPicker } from '@/components/SectionPicker'
import { SectionPicker } from '@/components/Elements/Universal/SectionPicker'
import { type Colors } from '@/stores/colors'
import flapMap from '@/stores/data/mensa-flags.json'
import { FoodFilterContext } from '@/stores/provider'
Expand Down
4 changes: 2 additions & 2 deletions src/app/(food)/preferences.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FormList from '@/components/FormList'
import { SectionPicker } from '@/components/SectionPicker'
import FormList from '@/components/Elements/Universal/FormList'
import { SectionPicker } from '@/components/Elements/Universal/SectionPicker'
import { type Colors } from '@/stores/colors'
import { FoodFilterContext } from '@/stores/provider'
import { type FormListSections } from '@customTypes/components'
Expand Down
6 changes: 3 additions & 3 deletions src/app/(map)/advanced.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import {
NoSessionError,
UnavailableSessionError,
} from '@/api/thi-session-handler'
import Divider from '@/components/Divider'
import { Dropdown } from '@/components/Map/Dropdown'
import { FreeRoomsList } from '@/components/Map/FreeRoomsList'
import Dropdown from '@/components/Elements/Map/Dropdown'
import { FreeRoomsList } from '@/components/Elements/Map/FreeRoomsList'
import Divider from '@/components/Elements/Universal/Divider'
import { type Colors } from '@/stores/colors'
import { formatISODate, formatISOTime } from '@/utils/date-utils'
import {
Expand Down
274 changes: 274 additions & 0 deletions src/app/(pages)/calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
import { NoSessionError } from '@/api/thi-session-handler'
import { EventRow, ExamRow } from '@/components/Elements/Pages/CalendarRow'
import Divider from '@/components/Elements/Universal/Divider'
import ToggleRow from '@/components/Elements/Universal/ToggleRow'
import { type Colors } from '@/stores/colors'
import { UserKindContext } from '@/stores/provider'
import { type Exam, calendar, loadExamList } from '@/utils/calendar-utils'
import { useTheme } from '@react-navigation/native'
import { useRouter } from 'expo-router'
import React, { useEffect, useState } from 'react'
import {
ActivityIndicator,
Linking,
ScrollView,
StyleSheet,
Text,
View,
} from 'react-native'
import { RefreshControl } from 'react-native-gesture-handler'

export default function CalendarPage(): JSX.Element {
const { userKind } = React.useContext(UserKindContext)
const [exams, setExams] = useState<Exam[]>([])
const colors = useTheme().colors as Colors
const displayTypes = ['Events', 'Exams']

const [selectedData, setSelectedData] = useState<string>('Events')

enum LoadingState {
LOADING,
LOADED,
ERROR,
REFRESHING,
}
const router = useRouter()
const [error, setError] = useState<Error | null>(null)
const [loadingState, setLoadingState] = useState(LoadingState.LOADING)
const primussUrl = 'https://www3.primuss.de/cgi-bin/login/index.pl?FH=fhin'
const calendarUrl =
'https://www.thi.de/en/international/studies/examination/semester-dates/'
const handleLinkPress = (): void => {
void Linking.openURL(
selectedData === 'Events' ? calendarUrl : primussUrl
)
}
useEffect(() => {
if (userKind === 'student') {
void loadEvents()
.then(() => {
setLoadingState(LoadingState.LOADED)
})
.catch((e) => {
if (e instanceof NoSessionError) {
router.replace('login')
} else {
console.error(e)
}

setError(e)
setLoadingState(LoadingState.ERROR)
})
} else {
setError(new Error('Not a student'))
setLoadingState(LoadingState.LOADED)
}
}, [userKind])

async function loadEvents(): Promise<void> {
const examList = await loadExamList()
setExams(examList)
}

const onRefresh: () => void = () => {
void loadEvents()
.then(() => {
setLoadingState(LoadingState.LOADED)
})
.catch((e) => {
setError(e)
setLoadingState(LoadingState.ERROR)
})
}

const data = selectedData === 'Events' ? calendar : exams
return (
<ScrollView
refreshControl={
loadingState !== LoadingState.LOADING &&
loadingState !== LoadingState.LOADED ? (
<RefreshControl
refreshing={loadingState === LoadingState.REFRESHING}
onRefresh={onRefresh}
/>
) : undefined
}
>
<>
<ToggleRow
items={displayTypes}
selectedElement={selectedData}
setSelectedElement={setSelectedData}
/>

<View
style={[
styles.itemsContainer,
{ backgroundColor: colors.card },
]}
>
{data.length > 0 ? (
data.map((item, index) => (
<React.Fragment key={index}>
{selectedData === 'Events' ? (
<EventRow event={item} colors={colors} />
) : (
<>
{loadingState ===
LoadingState.LOADED && (
<ExamRow
event={item}
colors={colors}
/>
)}
</>
)}
{index !== data.length - 1 && (
<Divider
color={colors.labelTertiaryColor}
width={'90%'}
/>
)}
</React.Fragment>
))
) : (
<>
{loadingState === LoadingState.LOADING && (
<View style={styles.loadingContainer}>
<ActivityIndicator
size="small"
color={colors.primary}
/>
</View>
)}
{userKind !== 'student' ? (
<View>
<Text
style={[
styles.errorMessage,
{ color: colors.text },
]}
>
{error?.message}
</Text>
<Text
style={[
styles.errorInfo,
{ color: colors.text },
]}
>
Sign in to see your exams.
</Text>
</View>
) : (
<>
{loadingState === LoadingState.ERROR && (
<View>
<Text
style={[
styles.errorMessage,
{ color: colors.text },
]}
>
{error?.message}
</Text>
<Text
style={[
styles.errorInfo,
{ color: colors.text },
]}
>
An error occurred while loading
the data.{'\n'}Pull down to
refresh.
</Text>
</View>
)}
{loadingState === LoadingState.LOADED && (
<View>
<Text
style={[
styles.errorMessage,
{ color: colors.text },
]}
>
No data found
</Text>
<Text
style={[
styles.errorInfo,
{ color: colors.text },
]}
>
Please try again later.
</Text>
</View>
)}
</>
)}
</>
)}
</View>
<View
style={{
width: '92%',
alignSelf: 'center',
paddingBottom: 50,
}}
>
<Text
style={{
fontSize: 12,
fontWeight: 'normal',
color: colors.labelColor,
paddingBottom: 25,
textAlign: 'justify',
}}
>
All information without guarantee. Binding information
is only available directly on the{' '}
<Text
style={{
color: colors.text,
textDecorationLine: 'underline',
}}
onPress={handleLinkPress}
>
university website
</Text>
.
</Text>
</View>
</>
</ScrollView>
)
}

const styles = StyleSheet.create({
errorMessage: {
paddingTop: 20,
fontWeight: '600',
fontSize: 16,
textAlign: 'center',
},
errorInfo: {
fontSize: 14,
textAlign: 'center',
marginTop: 10,
paddingBottom: 20,
},
loadingContainer: {
paddingTop: 40,
paddingBottom: 40,
justifyContent: 'center',
alignItems: 'center',
},
itemsContainer: {
alignSelf: 'center',
borderRadius: 8,
width: '95%',
marginTop: 14,
marginBottom: 20,
justifyContent: 'center',
},
})
Loading

0 comments on commit 25750dd

Please sign in to comment.