@olea-bps/components 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AppBar/index.js +83 -0
- package/AppBar/styles.js +27 -0
- package/BookDetail/index.js +268 -0
- package/BookDetail/styles.js +124 -0
- package/Component/342/200/216JobsFilter/index.js +188 -0
- package/Component/342/200/216JobsFilter/styles.js +12 -0
- package/ConnectivityWarning/index.js +65 -0
- package/ConnectivityWarning/styles.js +19 -0
- package/ContactDetail/index.js +232 -0
- package/ContactDetail/styles.js +32 -0
- package/CourseDetail/index.js +357 -0
- package/CourseDetail/styles.js +59 -0
- package/CourseDetailDialog/index.js +169 -0
- package/CourseDetailDialog/styles.js +116 -0
- package/CourseInfo/index.js +219 -0
- package/CourseInfo/styles.js +40 -0
- package/DevelopmentDialog/index.js +208 -0
- package/DevelopmentDialog/styles.js +10 -0
- package/EventCodeInput/index.js +146 -0
- package/EventCodeInput/styles.js +108 -0
- package/FlexMenuEntry/index.js +84 -0
- package/FlexMenuEntry/styles.js +27 -0
- package/MainMenuEntry/index.js +88 -0
- package/MainMenuEntry/styles.js +28 -0
- package/MealItem/index.js +87 -0
- package/MealItem/styles.js +73 -0
- package/MensaMenu/index.js +307 -0
- package/MensaMenu/styles.js +94 -0
- package/MensaSlider/index.js +184 -0
- package/MensaSlider/styles.js +53 -0
- package/Modal/index.js +106 -0
- package/Modal/styles.js +8 -0
- package/NewsDetail/index.js +377 -0
- package/NewsDetail/styles.js +77 -0
- package/NewsList/index.js +120 -0
- package/NewsList/styles.js +19 -0
- package/NewsListItem/index.js +89 -0
- package/NewsListItem/styles.js +32 -0
- package/OtherCourses/index.js +152 -0
- package/OtherCourses/styles.js +10 -0
- package/PtsDeparture/index.js +140 -0
- package/PtsDeparture/styles.js +7 -0
- package/PtsStation/index.js +183 -0
- package/PtsStation/styles.js +47 -0
- package/QuickLinks/index.js +127 -0
- package/QuickLinks/styles.js +45 -0
- package/RoomDetail/index.js +281 -0
- package/RoomDetail/styles.js +56 -0
- package/ScaledImage/index.js +92 -0
- package/SearchResults/index.js +362 -0
- package/SearchResults/styles.js +59 -0
- package/SettingSection/index.js +54 -0
- package/SettingSection/styles.js +15 -0
- package/SettingsDialog/index.js +52 -0
- package/SettingsDialog/styles.js +12 -0
- package/SettingsDialogRadio/index.js +66 -0
- package/SettingsDialogRadio/styles.js +7 -0
- package/SettingsDialogSelect/index.js +73 -0
- package/SettingsDialogSelect/styles.js +7 -0
- package/TimetableCodeInput/index.js +201 -0
- package/TimetableCodeInput/styles.js +28 -0
- package/TimetableDay/index.js +266 -0
- package/TimetableDay/styles.js +103 -0
- package/TimetableEvent/index.js +163 -0
- package/TimetableEvent/styles.js +108 -0
- package/TimetableList/index.js +116 -0
- package/TimetableList/styles.js +109 -0
- package/TimetableMonth/index.js +156 -0
- package/TimetableMonth/styles.js +29 -0
- package/TimetableWeek/index.js +245 -0
- package/TimetableWeek/styles.js +58 -0
- package/TopNews/index.js +282 -0
- package/TopNews/styles.js +125 -0
- package/TopNewsHtwk/index.js +279 -0
- package/TopNewsHtwk/styles.js +142 -0
- package/WebView/index.js +108 -0
- package/WebView/styles.js +11 -0
- package/index.js +39 -0
- package/package.json +37 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* You may obtain a copy of the License at
|
|
5
|
+
*
|
|
6
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
*
|
|
8
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
* See the License for the specific language governing permissions and
|
|
12
|
+
* limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import React, { useState, useMemo, useEffect } from 'react';
|
|
16
|
+
import {
|
|
17
|
+
StyleSheet,
|
|
18
|
+
Text,
|
|
19
|
+
TouchableOpacity,
|
|
20
|
+
View,
|
|
21
|
+
} from 'react-native';
|
|
22
|
+
|
|
23
|
+
import { useTheme } from 'react-native-paper';
|
|
24
|
+
import { useTranslation } from 'react-i18next';
|
|
25
|
+
import { useNavigation } from '@react-navigation/native';
|
|
26
|
+
|
|
27
|
+
import moment from 'moment';
|
|
28
|
+
import { DateTime } from 'luxon';
|
|
29
|
+
|
|
30
|
+
import IconsOpenasist from '@olea-bps/icons-openasist';
|
|
31
|
+
import { useDateCourses } from '@olea-bps/context-timetable';
|
|
32
|
+
|
|
33
|
+
import componentStyles from './styles';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Course Info Component
|
|
37
|
+
*
|
|
38
|
+
* Shows the informations of the next course, if the student has imported a timetable.
|
|
39
|
+
* If where is no timetable available, a notice will be displayed.
|
|
40
|
+
*
|
|
41
|
+
* Parameters:
|
|
42
|
+
* - none
|
|
43
|
+
*
|
|
44
|
+
* Navigation-Parameters:
|
|
45
|
+
* - none
|
|
46
|
+
*/
|
|
47
|
+
export default function CourseInfoComponent(props) {
|
|
48
|
+
const componentName = CourseInfoComponent.name;
|
|
49
|
+
const theme = useTheme();
|
|
50
|
+
const { colors } = theme;
|
|
51
|
+
const { t } = useTranslation();
|
|
52
|
+
const navigation = useNavigation();
|
|
53
|
+
|
|
54
|
+
const [now, setNow] = useState(moment());
|
|
55
|
+
|
|
56
|
+
const todayDate = DateTime.now().toISODate();
|
|
57
|
+
const academicQuarter = moment.duration(30, 'minutes');
|
|
58
|
+
|
|
59
|
+
// Vorlesungen von Stundenplan-Kontext abrufen, die heute stattfinden.
|
|
60
|
+
const [courses] = useDateCourses(todayDate);
|
|
61
|
+
|
|
62
|
+
console.debug(componentName, ': Count of courses: ', courses?.length ?? 0);
|
|
63
|
+
|
|
64
|
+
// Genieren der Styles, wenn nicht vorhanden oder das Theme sich geändert hat
|
|
65
|
+
const styles = useMemo(
|
|
66
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
67
|
+
[theme]
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const isoWeekDaysLabels = useMemo(
|
|
71
|
+
() => new Map(
|
|
72
|
+
['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']
|
|
73
|
+
.map(
|
|
74
|
+
(dayTranslationLabel, index) =>
|
|
75
|
+
[
|
|
76
|
+
index + 1,
|
|
77
|
+
{
|
|
78
|
+
short: t(`common:day${dayTranslationLabel}Short`),
|
|
79
|
+
long: t(`common:day${dayTranslationLabel}`),
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
)
|
|
83
|
+
),
|
|
84
|
+
[t]
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// Suchen der nächsten Vorlesung
|
|
88
|
+
const nextCourse = courses
|
|
89
|
+
// Berechnen der heutigen Startzeitpunkt der Vorlesung als Datetime und speichern als startMoment-Property.
|
|
90
|
+
// Berechnen der heutigen mit academischen Viertel der Vorlesung als Datetime und speichern als academicQuarterMoment-Property.
|
|
91
|
+
?.map?.(course => {
|
|
92
|
+
// Holen der Uhrzeit der Vorlesung
|
|
93
|
+
const courseTime = course.times[0];
|
|
94
|
+
// Erstellen der datetime aus dem heutigen Datum und der Enduhrzeit
|
|
95
|
+
const courseStartMoment = moment(courseTime.start, 'HH:mm');
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
...course,
|
|
99
|
+
startMoment: courseStartMoment,
|
|
100
|
+
academicQuarterMoment: courseStartMoment.add(academicQuarter),
|
|
101
|
+
// Ursprüngliche Vorlesung als eigene Instanz einbetten
|
|
102
|
+
// Diese Instanze sollte als Abhängigkeit für Hooks verwendet werden, hier bei jedem Rendern eine neue Instaze entsteht
|
|
103
|
+
origineCourse: course,
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
// Sortieren der Vorlesung nach ihren Start-Datetimes. unix()-Methode gibt einen Integer-Zeitstempel zurück.
|
|
107
|
+
?.sort?.((courseA, courseB) => courseA.startMoment.unix() - courseB.startMoment.unix())
|
|
108
|
+
// Suche die erste Vorlesung, anhand der Startzeit mit academischen Viertel nach Jetzt ist.
|
|
109
|
+
?.find?.(course => course.academicQuarterMoment.isAfter(now));
|
|
110
|
+
|
|
111
|
+
const nextOrigineCourse = nextCourse?.origineCourse
|
|
112
|
+
|
|
113
|
+
console.debug(componentName, ': next course:', nextCourse);
|
|
114
|
+
|
|
115
|
+
// now neu setzen, wenn die nächste Vorlesung anfängt oder der Tag zu Ende ist
|
|
116
|
+
useEffect(
|
|
117
|
+
() => {
|
|
118
|
+
// Millisekunden bis zum anzeigen der nächste Vorlesung bzw. des Tagesende ausrechnen und den mathematischen Betrag nehmen
|
|
119
|
+
const timeoutToCalculateNextCourse = Math.abs(
|
|
120
|
+
moment().diff(
|
|
121
|
+
// Prüfen ob es heute noch eine Vorlesung geben wird
|
|
122
|
+
nextCourse
|
|
123
|
+
// Es wird der Anfang mit academischen Viertel der nächsten/aktuellen Vorlesung genommen
|
|
124
|
+
? nextCourse.academicQuarterMoment
|
|
125
|
+
// Es wird das Ende des heutigen Tages genommen
|
|
126
|
+
: moment().endOf('day')
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
console.debug(componentName, ': recalculating next course in ', timeoutToCalculateNextCourse, ' miliseconds');
|
|
131
|
+
|
|
132
|
+
// Timer setzen und Handel zum Löschen des Timeouts zwischenspeichern
|
|
133
|
+
const timeoutHandle = setTimeout(() => setNow(moment()), timeoutToCalculateNextCourse);
|
|
134
|
+
return () => {
|
|
135
|
+
// Timer löschen, falls die nächste Vorlesung wechselt
|
|
136
|
+
clearTimeout(timeoutHandle);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
[nextOrigineCourse]
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const errorMessage = !nextOrigineCourse && courses?.length ? t('course:noLecturesAvailable') : t('course:noLectureFound');
|
|
143
|
+
|
|
144
|
+
return useMemo(
|
|
145
|
+
() => {
|
|
146
|
+
const courseTitle = nextOrigineCourse?.title?.data;
|
|
147
|
+
const courseTime = nextOrigineCourse?.times?.[0];
|
|
148
|
+
const courseIsoDayOfWeek = courseTime?.dayOfWeek;
|
|
149
|
+
const courseWeekDaysLabels = isoWeekDaysLabels.get(courseIsoDayOfWeek);
|
|
150
|
+
|
|
151
|
+
const courseTimeStart = courseTime?.start;
|
|
152
|
+
const courseTimeStartAccessibility = courseTimeStart
|
|
153
|
+
? courseTimeStart.slice(0, 2) + ' ' + t('accessibility:pts:speechTime') + ' ' + (courseTimeStart.slice(3, 5) !== '00' ? courseTimeStart.slice(3, 5) : '') + ','
|
|
154
|
+
: null;
|
|
155
|
+
|
|
156
|
+
const courseTimeEnd = courseTime?.end;
|
|
157
|
+
const coursetimeEndAccessibility = courseTimeEnd
|
|
158
|
+
? courseTimeEnd.slice(0, 2) + ' ' + t('accessibility:pts:speechTime') + ' ' + (courseTimeEnd.slice(3, 5) !== '00' ? courseTimeEnd.slice(3, 5) : '') + ','
|
|
159
|
+
: null;
|
|
160
|
+
|
|
161
|
+
const courseDayOfWeek = courseWeekDaysLabels?.short;
|
|
162
|
+
const courseDayOfWeekAccessibility = courseWeekDaysLabels?.long;
|
|
163
|
+
|
|
164
|
+
const courseTimeText = courseDayOfWeek && courseTimeStart && courseTimeEnd
|
|
165
|
+
? `${courseDayOfWeek} ${courseTimeStart} - ${courseTimeEnd}`
|
|
166
|
+
: t('common:noDetails');
|
|
167
|
+
|
|
168
|
+
const courseTimeAccessibility = courseDayOfWeekAccessibility && courseTimeStartAccessibility && coursetimeEndAccessibility
|
|
169
|
+
? `${courseDayOfWeekAccessibility} ${t('accessibility:timetable:at')} ${courseTimeStartAccessibility} ${t('accessibility:timetable:to')} ${coursetimeEndAccessibility}`
|
|
170
|
+
: null;
|
|
171
|
+
|
|
172
|
+
const courseRoom = Array.isArray(nextOrigineCourse?.room)
|
|
173
|
+
? nextOrigineCourse?.room?.[0]?.data
|
|
174
|
+
: nextOrigineCourse?.room?.data;
|
|
175
|
+
|
|
176
|
+
const courseRoomText = courseRoom?.replace(/\([\w\W]+/, '') ?? t('common:noDetails');
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<TouchableOpacity
|
|
180
|
+
onPress={
|
|
181
|
+
() => navigation.navigate(
|
|
182
|
+
'timetableModule',
|
|
183
|
+
nextOrigineCourse
|
|
184
|
+
? { course: nextOrigineCourse }
|
|
185
|
+
: { showCodeInput: true }
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
accessible={true}
|
|
189
|
+
accessibilityLabel={t('course:nextLecture')}
|
|
190
|
+
accessibilityHint={
|
|
191
|
+
nextOrigineCourse
|
|
192
|
+
? [courseTitle, courseTimeAccessibility, courseRoom].join(' ')
|
|
193
|
+
: errorMessage
|
|
194
|
+
}
|
|
195
|
+
>
|
|
196
|
+
<View style={styles.container}>
|
|
197
|
+
<Text style={styles.headline}>{t('course:nextLecture')}</Text>
|
|
198
|
+
</View>
|
|
199
|
+
<View style={styles.innerContainer}>
|
|
200
|
+
<View style={styles.iconContainer}>
|
|
201
|
+
<IconsOpenasist icon={"timetable"} color={colors.black} size={40} />
|
|
202
|
+
</View>
|
|
203
|
+
{
|
|
204
|
+
nextOrigineCourse
|
|
205
|
+
? <View style={styles.contentContainer}>
|
|
206
|
+
<Text style={styles.title}>{courseTitle}</Text>
|
|
207
|
+
<Text style={styles.title}>{courseTimeText}{courseTimeText && courseRoomText ? ' | ' : ''}{courseRoomText}</Text>
|
|
208
|
+
</View>
|
|
209
|
+
: <View style={styles.contentContainer}>
|
|
210
|
+
<Text style={styles.title}>{errorMessage}</Text>
|
|
211
|
+
</View>
|
|
212
|
+
}
|
|
213
|
+
</View>
|
|
214
|
+
</TouchableOpacity>
|
|
215
|
+
)
|
|
216
|
+
},
|
|
217
|
+
[nextOrigineCourse, errorMessage, navigation, styles, t, colors]
|
|
218
|
+
);
|
|
219
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export default function(theme) {
|
|
2
|
+
return {
|
|
3
|
+
container: {
|
|
4
|
+
flex: 1,
|
|
5
|
+
flexDirection: 'row',
|
|
6
|
+
alignItems: 'center',
|
|
7
|
+
paddingHorizontal: theme.paddings.default,
|
|
8
|
+
paddingVertical: theme.paddings.small,
|
|
9
|
+
paddingBottom: 0
|
|
10
|
+
},
|
|
11
|
+
innerContainer: {
|
|
12
|
+
flex: 1,
|
|
13
|
+
flexDirection: 'row',
|
|
14
|
+
paddingVertical: theme.paddings.small,
|
|
15
|
+
paddingHorizontal: theme.paddings.default,
|
|
16
|
+
|
|
17
|
+
},
|
|
18
|
+
iconContainer: {
|
|
19
|
+
paddingTop: theme.paddings.xsmall / 2,
|
|
20
|
+
paddingBottom: theme.paddings.xsmall / 2,
|
|
21
|
+
paddingRight: theme.paddings.small,
|
|
22
|
+
},
|
|
23
|
+
contentContainer: {
|
|
24
|
+
flex: 1,
|
|
25
|
+
padding: theme.paddings.xsmall
|
|
26
|
+
},
|
|
27
|
+
headline: {
|
|
28
|
+
...theme.fonts.regular,
|
|
29
|
+
fontSize: theme.fontSizes.m,
|
|
30
|
+
textTransform: 'uppercase'
|
|
31
|
+
},
|
|
32
|
+
title: {
|
|
33
|
+
...theme.fonts.regular,
|
|
34
|
+
fontSize: theme.fontSizes.m,
|
|
35
|
+
},
|
|
36
|
+
modalContainer: {
|
|
37
|
+
flex: 1
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* You may obtain a copy of the License at
|
|
5
|
+
*
|
|
6
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
*
|
|
8
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
* See the License for the specific language governing permissions and
|
|
12
|
+
* limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useMemo, useCallback, FC } from 'react';
|
|
16
|
+
import {
|
|
17
|
+
StyleSheet,
|
|
18
|
+
Text,
|
|
19
|
+
View
|
|
20
|
+
} from 'react-native';
|
|
21
|
+
|
|
22
|
+
import { useDispatch } from 'react-redux';
|
|
23
|
+
import { useTheme, Portal, Dialog, Checkbox, Button } from 'react-native-paper';
|
|
24
|
+
import { useTranslation } from 'react-i18next';
|
|
25
|
+
|
|
26
|
+
import { onSettingDevelopOverride, useActiveStagingMenuItems, useShowDeeplinkAlert, useStagingServer, useSubscriberId } from '@olea-bps/core';
|
|
27
|
+
|
|
28
|
+
import componentStyles from './styles'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Ein Dialog, welches Optionen für Entwickler anzeigt.
|
|
32
|
+
* Der Dialog verwendet sein eigenes {@link Portal}, sodass nur die Einbindung dieser Komponente für die richtige Funktionweise nötig ist.
|
|
33
|
+
*
|
|
34
|
+
* Über diesen Dialog, kann auf den Staging-Server umgeschaltet werden.
|
|
35
|
+
*
|
|
36
|
+
* @param {object} props
|
|
37
|
+
* @param {boolean} props.visible Wird der Dialog angezeigt?
|
|
38
|
+
* @param {() => void} [props.onDismiss] Callback-Funktion welche ausgeführt wird, wenn der Dialog geschlossen werden soll.
|
|
39
|
+
* @returns {FC}
|
|
40
|
+
*/
|
|
41
|
+
export default function DevelopmentDialog({ visible, onDismiss }) {
|
|
42
|
+
const { t } = useTranslation();
|
|
43
|
+
const theme = useTheme();
|
|
44
|
+
const { colors, appSettings } = theme;
|
|
45
|
+
const mainMenuSettings = appSettings?.mainMenu;
|
|
46
|
+
const menuItems = mainMenuSettings?.items ?? {};
|
|
47
|
+
const menuRoutes = mainMenuSettings?.routes ?? [];
|
|
48
|
+
const isStagingServerActive = useStagingServer();
|
|
49
|
+
const subscriberId = useSubscriberId();
|
|
50
|
+
const showDeeplinkAlert = useShowDeeplinkAlert();
|
|
51
|
+
const activeStagingMenuItems = useActiveStagingMenuItems();
|
|
52
|
+
const dispatch = useDispatch();
|
|
53
|
+
|
|
54
|
+
const styles = useMemo(
|
|
55
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
56
|
+
[theme, componentStyles]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
// Erzeugen einer Funktion, die die Entwicklereinstellugen setzt.
|
|
60
|
+
// Dabei werden immer die derzeitigen Werte gesetzt, außer es werden neue Werte zum überschreiben übergeben.
|
|
61
|
+
// Diese Funktion sollte langfristig entfallen.
|
|
62
|
+
const setDevelopmentSettings = useCallback(
|
|
63
|
+
(settings) => dispatch(
|
|
64
|
+
onSettingDevelopOverride(
|
|
65
|
+
'settingsDevelop',
|
|
66
|
+
{
|
|
67
|
+
useStaging: isStagingServerActive,
|
|
68
|
+
showDeeplinkAlert: showDeeplinkAlert,
|
|
69
|
+
activeStagingMenuItems: activeStagingMenuItems,
|
|
70
|
+
...settings,
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
),
|
|
74
|
+
[isStagingServerActive, showDeeplinkAlert, activeStagingMenuItems, onSettingDevelopOverride]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
const output = [];
|
|
78
|
+
|
|
79
|
+
output.push(
|
|
80
|
+
<View key={'item_use_stg'} style={styles.selectOption}>
|
|
81
|
+
<Text>{t('settings:develop.useStagingServer')}</Text>
|
|
82
|
+
<Checkbox.Android status={isStagingServerActive ? 'checked' : 'unchecked'}
|
|
83
|
+
onPress={() => setDevelopmentSettings({ useStaging: !isStagingServerActive })}
|
|
84
|
+
color={colors.checkboxChecked}
|
|
85
|
+
uncheckedColor={colors.checkboxUnchecked}
|
|
86
|
+
/>
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
output.push(
|
|
91
|
+
<View key={'item_notification_subscriber'} style={[{ marginBottom: 30 }]}>
|
|
92
|
+
<Text>{t('settings:develop.notificationSubscriberId')}</Text>
|
|
93
|
+
<Text>{subscriberId}</Text>
|
|
94
|
+
</View>
|
|
95
|
+
);
|
|
96
|
+
output.push(
|
|
97
|
+
<View key={'item_notification_push_message'} style={[{ marginBottom: 30 }]}>
|
|
98
|
+
<Text>{t('settings:develop.resetPushMessage')}</Text>
|
|
99
|
+
<Button
|
|
100
|
+
buttonColor={colors.primary}
|
|
101
|
+
textColor={colors.buttonText}
|
|
102
|
+
onPress={() => dispatch({ type: 'UPDATE_PUSH_MESSAGE_SHOWN', pushMessageShown: false })}
|
|
103
|
+
>
|
|
104
|
+
Reset
|
|
105
|
+
</Button>
|
|
106
|
+
</View>
|
|
107
|
+
);
|
|
108
|
+
output.push(
|
|
109
|
+
<View key={'item_deeplink_show_url'} style={[{ marginBottom: 30 }, styles.selectOption]}>
|
|
110
|
+
<Text>{t('settings:develop.showDeepLink')}</Text>
|
|
111
|
+
<Checkbox
|
|
112
|
+
status={showDeeplinkAlert ? 'checked' : 'unchecked'}
|
|
113
|
+
onPress={() => setDevelopmentSettings({ showDeeplinkAlert: !showDeeplinkAlert })}
|
|
114
|
+
color={colors.checkboxChecked}
|
|
115
|
+
uncheckedColor={colors.checkboxUnchecked}
|
|
116
|
+
></Checkbox>
|
|
117
|
+
</View>
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Für jeden Menü-Tab einen Bereich erstellen und im Bereich für jeden Menüpunkt eine Checkbox anbieten.
|
|
121
|
+
// Menüpunkte-Objekt in ein Array umwandeln
|
|
122
|
+
const activatableMenuItemsSettings = Object.entries(menuItems)
|
|
123
|
+
// Aus der Liste der Menüpunkten, werden die Menüpunkte herausgefiltert, die immer aktiv/eingeschaltet sind.
|
|
124
|
+
.map(
|
|
125
|
+
([menuItemKey, menuItemEntries]) => {
|
|
126
|
+
const stagingMenuItemEntries = menuItemEntries.filter(
|
|
127
|
+
menuItemEntry =>
|
|
128
|
+
// Wenn active-Property nicht vorhanden, ist der Menüpunkt activ, dann Negierung, weil nicht aktive Menueinträge gefiltert werden
|
|
129
|
+
!(menuItemEntry?.active ?? true)
|
|
130
|
+
);
|
|
131
|
+
return [menuItemKey, stagingMenuItemEntries];
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
// Es werden Menü-Tabs herausgefiltert, die keine Menüpunkte, die nur im Stagingmodus zu sehen sind.
|
|
135
|
+
.filter(
|
|
136
|
+
([menuItemKey, stagingMenuItemEntries]) => (stagingMenuItemEntries?.length ?? 0) > 0
|
|
137
|
+
)
|
|
138
|
+
// Rendern der Menü-Tabs und deren Menüeinträge
|
|
139
|
+
.map(
|
|
140
|
+
([menuItemKey, stagingMenuItemEntries]) => {
|
|
141
|
+
const mainMenuRouteTitleKey = menuRoutes
|
|
142
|
+
?.find(menuRoute => menuRoute?.key === menuItemKey)
|
|
143
|
+
?.title;
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
< View key={`item_menuitems_${menuItemKey}`} >
|
|
147
|
+
<Text>{t(mainMenuRouteTitleKey)}-Tab</Text>
|
|
148
|
+
{
|
|
149
|
+
// Rendern der Menüpunkte eines Menü-Tabs
|
|
150
|
+
stagingMenuItemEntries.map(
|
|
151
|
+
stagingMenuItemEntry => {
|
|
152
|
+
const stagingMenuItemEntryKey = `${stagingMenuItemEntry?.title}`;
|
|
153
|
+
|
|
154
|
+
// Der Menüpunkt ist aktiv, wenn sein Schlüssel in der Liste der activen Menüpunkte zu finden ist
|
|
155
|
+
const isActive = activeStagingMenuItems.includes(stagingMenuItemEntryKey);
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<Checkbox.Item
|
|
159
|
+
key={stagingMenuItemEntryKey}
|
|
160
|
+
label={t(stagingMenuItemEntry?.title)}
|
|
161
|
+
status={isActive ? 'checked' : 'unchecked'}
|
|
162
|
+
color={colors.checkboxChecked}
|
|
163
|
+
uncheckedColor={colors.checkboxUnchecked}
|
|
164
|
+
theme={theme}
|
|
165
|
+
style={styles.selectOption}
|
|
166
|
+
onPress={() => setDevelopmentSettings(
|
|
167
|
+
{
|
|
168
|
+
activeStagingMenuItems: isActive
|
|
169
|
+
// Wenn der Menüpunkt schon aktiv ist, wird er aus der Liste der aktiven Punkte entfernt.
|
|
170
|
+
? activeStagingMenuItems.filter(activeStagingMenuItem => activeStagingMenuItem !== stagingMenuItemEntryKey)
|
|
171
|
+
// Wenn der Menüpunkt nicht aktiv ist, wird er zur Liste der aktiven Punkte hinzugefügt.
|
|
172
|
+
: [...activeStagingMenuItems, stagingMenuItemEntryKey]
|
|
173
|
+
}
|
|
174
|
+
)}
|
|
175
|
+
/>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
</View>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Portal>
|
|
187
|
+
<Dialog visible={visible} onDismiss={onDismiss}>
|
|
188
|
+
<Dialog.Title>{t('settings:develop.dialog')}</Dialog.Title>
|
|
189
|
+
<Dialog.Content>
|
|
190
|
+
{
|
|
191
|
+
[
|
|
192
|
+
...output,
|
|
193
|
+
...activatableMenuItemsSettings,
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
</Dialog.Content>
|
|
197
|
+
<Dialog.Actions>
|
|
198
|
+
<Button
|
|
199
|
+
onPress={onDismiss}
|
|
200
|
+
textColor={colors.buttonText}
|
|
201
|
+
>
|
|
202
|
+
{t('common:okLabel')}
|
|
203
|
+
</Button>
|
|
204
|
+
</Dialog.Actions>
|
|
205
|
+
</Dialog>
|
|
206
|
+
</Portal>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* You may obtain a copy of the License at
|
|
5
|
+
*
|
|
6
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
*
|
|
8
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
* See the License for the specific language governing permissions and
|
|
12
|
+
* limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useMemo, useState } from 'react';
|
|
16
|
+
import {
|
|
17
|
+
ActivityIndicator,
|
|
18
|
+
KeyboardAvoidingView,
|
|
19
|
+
ScrollView,
|
|
20
|
+
StyleSheet,
|
|
21
|
+
TouchableOpacity,
|
|
22
|
+
View,
|
|
23
|
+
Linking,
|
|
24
|
+
Text
|
|
25
|
+
} from 'react-native';
|
|
26
|
+
|
|
27
|
+
import { useTranslation } from 'react-i18next';
|
|
28
|
+
import {
|
|
29
|
+
Headline,
|
|
30
|
+
Paragraph,
|
|
31
|
+
TextInput,
|
|
32
|
+
useTheme,
|
|
33
|
+
} from 'react-native-paper';
|
|
34
|
+
|
|
35
|
+
import { usePersonalEvents, usePersonalEventsCode } from '@olea-bps/context-event';
|
|
36
|
+
|
|
37
|
+
import componentStyles from './styles';
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* View Name
|
|
42
|
+
*
|
|
43
|
+
* Shows the View
|
|
44
|
+
*
|
|
45
|
+
* Parameters:
|
|
46
|
+
* - none
|
|
47
|
+
*
|
|
48
|
+
* Navigation-Parameters:
|
|
49
|
+
* - none
|
|
50
|
+
*/
|
|
51
|
+
export default function EventCodeInputComponent({ onClose, onPersonalEventsImported }) {
|
|
52
|
+
const theme = useTheme();
|
|
53
|
+
const { colors } = theme;
|
|
54
|
+
const { t } = useTranslation();
|
|
55
|
+
|
|
56
|
+
const [, refreshPersonalEvents] = usePersonalEvents();
|
|
57
|
+
const [personalEventsCode, setPersonalEventsCode] = usePersonalEventsCode();
|
|
58
|
+
const [personalEventsImportedImporting, setPersonalEventsImportedImporting] = useState(false);
|
|
59
|
+
const [personalEventsCodeInput, setPersonalEventsCodeInput] = useState(personalEventsCode ? personalEventsCode : '');
|
|
60
|
+
|
|
61
|
+
const styles = useMemo(
|
|
62
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
63
|
+
[theme]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const handleImportButtonPressed = () => {
|
|
67
|
+
setPersonalEventsImportedImporting(true);
|
|
68
|
+
|
|
69
|
+
if (personalEventsCodeInput.length < 8) {
|
|
70
|
+
alert(t('eventCode:codeShortError'));
|
|
71
|
+
setPersonalEventsImportedImporting(false);
|
|
72
|
+
} else if (personalEventsCodeInput.length > 8) {
|
|
73
|
+
alert(t('eventCode:codeLongError'));
|
|
74
|
+
setPersonalEventsImportedImporting(false);
|
|
75
|
+
} else {
|
|
76
|
+
setPersonalEventsCode(personalEventsCodeInput);
|
|
77
|
+
refreshPersonalEvents()
|
|
78
|
+
.then(() => setPersonalEventsImportedImporting(false))
|
|
79
|
+
.then(() => onPersonalEventsImported?.());
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<ScrollView style={styles.container}>
|
|
85
|
+
<KeyboardAvoidingView behavior={"padding"} >
|
|
86
|
+
<View style={styles.paragraph}>
|
|
87
|
+
<Text style={styles.paragraphText} selectable={true} dataDetectorType={'link'}>
|
|
88
|
+
{
|
|
89
|
+
personalEventsCodeInput
|
|
90
|
+
? t('eventCode:inputEventCode')
|
|
91
|
+
: <Text style={styles.paragraphText} selectable={true} dataDetectorType={'link'}>
|
|
92
|
+
{t('eventCode:notImportedYet')}
|
|
93
|
+
<Text style={styles.linkText} selctable={true} dataDetectorType={'link'} onPress={() => Linking.openURL('https://www.tu-chemnitz.de/tu/veranstaltungen/tuctag/alle-angebote.html')}>
|
|
94
|
+
{t('eventCode:tucDayLinkText')}
|
|
95
|
+
</Text>
|
|
96
|
+
</Text>
|
|
97
|
+
}
|
|
98
|
+
</Text>
|
|
99
|
+
</View>
|
|
100
|
+
<View style={styles.importTextInput}>
|
|
101
|
+
<TextInput
|
|
102
|
+
theme={{ colors: { primary: colors.textInput, placeholder: colors.textLighter } }}
|
|
103
|
+
style={styles.paragraphText}
|
|
104
|
+
maxLength={8}
|
|
105
|
+
selectionColor={colors.textInputSelection}
|
|
106
|
+
label={t('eventCode:inputPlaceholder')}
|
|
107
|
+
value={personalEventsCodeInput}
|
|
108
|
+
onChangeText={text => setPersonalEventsCodeInput(text.trim())}
|
|
109
|
+
/>
|
|
110
|
+
</View>
|
|
111
|
+
|
|
112
|
+
<View style={styles.container}>
|
|
113
|
+
<TouchableOpacity
|
|
114
|
+
style={styles.importButton}
|
|
115
|
+
labelStyle={styles.importButtonLabel}
|
|
116
|
+
label={t('eventCode:importButton')}
|
|
117
|
+
onPress={handleImportButtonPressed}
|
|
118
|
+
>
|
|
119
|
+
<Headline style={styles.importButtonLabel} color={colors.buttonText}>
|
|
120
|
+
{t('eventCode:importButton')}
|
|
121
|
+
</Headline>
|
|
122
|
+
</TouchableOpacity>
|
|
123
|
+
<TouchableOpacity
|
|
124
|
+
style={styles.continueWithoutCodeButton}
|
|
125
|
+
labelStyle={styles.importButtonLabel}
|
|
126
|
+
label={t('eventCode:importButton')}
|
|
127
|
+
onPress={() => onClose?.()}>
|
|
128
|
+
<Headline style={styles.continueWithoutCodeButtonLabel} color={colors.buttonText}>{t('eventCode:continueWithoutCodeButton')}</Headline>
|
|
129
|
+
</TouchableOpacity>
|
|
130
|
+
</View>
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
{
|
|
134
|
+
personalEventsImportedImporting
|
|
135
|
+
? <ActivityIndicator style={styles.activity} size="large" color={colors.loadingIndicator} />
|
|
136
|
+
: null
|
|
137
|
+
}
|
|
138
|
+
{
|
|
139
|
+
personalEventsImportedImporting
|
|
140
|
+
? <View style={styles.paragraph}><Paragraph>{t('timetable:importInProgress', '')}</Paragraph></View>
|
|
141
|
+
: null
|
|
142
|
+
}
|
|
143
|
+
</KeyboardAvoidingView>
|
|
144
|
+
</ScrollView>
|
|
145
|
+
);
|
|
146
|
+
}
|