@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,307 @@
|
|
|
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
|
+
Text,
|
|
18
|
+
StyleSheet,
|
|
19
|
+
View,
|
|
20
|
+
Image
|
|
21
|
+
} from 'react-native';
|
|
22
|
+
import { connect } from 'react-redux'
|
|
23
|
+
import { Button, Dialog, Portal, useTheme } from 'react-native-paper';
|
|
24
|
+
import { useTranslation } from 'react-i18next';
|
|
25
|
+
|
|
26
|
+
import { useFilteredMenu } from '@olea-bps/context-canteen';
|
|
27
|
+
|
|
28
|
+
import { onUpdateRefreshing } from "@olea-bps/core";
|
|
29
|
+
import IconsOpenasist from "@olea-bps/icons-openasist";
|
|
30
|
+
|
|
31
|
+
import componentStyles from './styles';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Mensa Menu Component
|
|
35
|
+
*
|
|
36
|
+
* Shows a list of all menu items of selected canteen
|
|
37
|
+
* The class component uses hooks in order to render each meal component and each dialog as its own component
|
|
38
|
+
* instead of the whole menu
|
|
39
|
+
*
|
|
40
|
+
* In this component two hooks are used:
|
|
41
|
+
* - Meal that renders each meal
|
|
42
|
+
* - AdditionalDialog that renders the dialog containing all additives and allergens if there are any
|
|
43
|
+
*
|
|
44
|
+
* Parameters:
|
|
45
|
+
* - none
|
|
46
|
+
*
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
function SelectionIcons({ selections, iconSize }) {
|
|
51
|
+
const theme = useTheme();
|
|
52
|
+
const { colors } = theme;
|
|
53
|
+
const styles = useMemo(
|
|
54
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
55
|
+
[theme, componentStyles]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<View style={styles.mealItemTitleIcons}>
|
|
60
|
+
{
|
|
61
|
+
selections.map(
|
|
62
|
+
selection =>
|
|
63
|
+
<View key={selection} style={{ marginLeft: 2, lineHeight: 20 }}>
|
|
64
|
+
<IconsOpenasist icon={"mensa-" + selection} color={colors.secondaryText} size={iconSize} />
|
|
65
|
+
</View>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
</View>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function AdditionalDialog({ allergens, additives, visible, onDismiss }) {
|
|
73
|
+
const { t } = useTranslation();
|
|
74
|
+
const theme = useTheme();
|
|
75
|
+
const { colors } = theme;
|
|
76
|
+
const styles = useMemo(
|
|
77
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
78
|
+
[theme, componentStyles]
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Portal>
|
|
83
|
+
<Dialog
|
|
84
|
+
visible={visible}
|
|
85
|
+
onDismiss={onDismiss}>
|
|
86
|
+
<Dialog.Title style={styles.dialogTitle}>{t('canteen:allergeneTitle')}</Dialog.Title>
|
|
87
|
+
<Dialog.Content>
|
|
88
|
+
{
|
|
89
|
+
Array.isArray(allergens)
|
|
90
|
+
? <View style={styles.addionals}>
|
|
91
|
+
<Text style={styles.dialogContent}>{t('canteen:allergens:title')}</Text>
|
|
92
|
+
{
|
|
93
|
+
allergens.map(
|
|
94
|
+
allergen =>
|
|
95
|
+
<Text key={allergen} style={styles.dialogContent}>{t('canteen:allergens:' + allergen)}</Text>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
</View>
|
|
99
|
+
: null
|
|
100
|
+
}
|
|
101
|
+
{
|
|
102
|
+
Array.isArray(additives)
|
|
103
|
+
? <View style={styles.addionals}>
|
|
104
|
+
<Text style={styles.dialogContent}>{t('canteen:additives:title')}</Text>
|
|
105
|
+
{
|
|
106
|
+
additives.map(
|
|
107
|
+
additive =>
|
|
108
|
+
<Text key={additive} style={styles.dialogContent}>{t('canteen:additives:' + additive)}</Text>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
</View>
|
|
112
|
+
: null
|
|
113
|
+
}
|
|
114
|
+
</Dialog.Content>
|
|
115
|
+
<Dialog.Actions>
|
|
116
|
+
<Button labelStyle={styles.dialogContent} onPress={onDismiss} color={colors.buttonText}>
|
|
117
|
+
{t('common:okLabel')}
|
|
118
|
+
</Button>
|
|
119
|
+
</Dialog.Actions>
|
|
120
|
+
</Dialog>
|
|
121
|
+
</Portal>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
function Meal({ settings, meal, priceGroupCode, priceGroupName }) {
|
|
127
|
+
/**
|
|
128
|
+
* @param props contains the current meal, styles, themestyles and local styles
|
|
129
|
+
* @returns {*} a component for each meal(including dialog) with its own state
|
|
130
|
+
*
|
|
131
|
+
* The main-motivation for using hooks is that the state can be set for each meal instead of setting it for every menu.
|
|
132
|
+
* Otherwise the dialog rendered by the AdditionalDialog-function would be set to visible for very meal in the menu at once
|
|
133
|
+
* resulting in the background becoming completely dark when the dialog is opened and other style-related issues
|
|
134
|
+
* By using hooks, the visibility can be set for each individual meal.
|
|
135
|
+
* The function is called in the render function of the class-component inside the flatlist-component for every single meal
|
|
136
|
+
* AdditionalDialog is also another hook on its own and is defined above this function
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
const { t } = useTranslation();
|
|
140
|
+
const theme = useTheme();
|
|
141
|
+
const { themeStyles, colors } = theme;
|
|
142
|
+
const styles = useMemo(
|
|
143
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
144
|
+
[theme, componentStyles]
|
|
145
|
+
);
|
|
146
|
+
const language = settings.settingsGeneral.language;
|
|
147
|
+
|
|
148
|
+
const mealPrice = useMemo(
|
|
149
|
+
() => {
|
|
150
|
+
const mealPrice = meal?.prices?.[priceGroupCode];
|
|
151
|
+
|
|
152
|
+
return typeof mealPrice === 'number'
|
|
153
|
+
? `${mealPrice.toFixed(2).toLocaleString(language ?? 'de')} €`
|
|
154
|
+
: mealPrice;
|
|
155
|
+
|
|
156
|
+
},
|
|
157
|
+
[meal, meal?.prices, priceGroupCode, language]
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
/*
|
|
161
|
+
The const visible is a state defined for each single Meal-Component and can be set by teh setVisible-Fucntion
|
|
162
|
+
item contains all information for the current meal. It is still unfortunate that the filtering Process is done while rendering
|
|
163
|
+
and not before
|
|
164
|
+
*/
|
|
165
|
+
const [additionalDialogVisible, setAdditionalDialogVisible] = useState(false);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<View style={themeStyles.flexRow}>
|
|
169
|
+
<View style={[themeStyles.cardContent, styles.mealContent]}>
|
|
170
|
+
{
|
|
171
|
+
meal?.imageUrl
|
|
172
|
+
? <Image
|
|
173
|
+
style={styles.mealImage}
|
|
174
|
+
source={{ uri: meal.imageUrl }}
|
|
175
|
+
resizeMode="contain"
|
|
176
|
+
/>
|
|
177
|
+
: null
|
|
178
|
+
}
|
|
179
|
+
<View style={styles.mealItemTitle}>
|
|
180
|
+
<Text style={styles.mealItemTitleText}>
|
|
181
|
+
{meal.category}
|
|
182
|
+
</Text>
|
|
183
|
+
<Text>
|
|
184
|
+
<SelectionIcons
|
|
185
|
+
selections={meal?.selections ?? []}
|
|
186
|
+
iconSize={styles.mealItemTitleText.lineHeight}
|
|
187
|
+
/>
|
|
188
|
+
</Text>
|
|
189
|
+
</View>
|
|
190
|
+
<Text style={styles.mealItemText}>
|
|
191
|
+
{meal.title}
|
|
192
|
+
</Text>
|
|
193
|
+
<View style={styles.addionalInformation}>
|
|
194
|
+
<Text style={styles.mealItemPrice}>
|
|
195
|
+
{
|
|
196
|
+
priceGroupName && mealPrice
|
|
197
|
+
? `${priceGroupName}: ${mealPrice}`
|
|
198
|
+
: null
|
|
199
|
+
}
|
|
200
|
+
</Text>
|
|
201
|
+
{
|
|
202
|
+
meal?.additives || meal?.allergens
|
|
203
|
+
? <>
|
|
204
|
+
<Button
|
|
205
|
+
style={styles.btnAddionals}
|
|
206
|
+
color={colors.buttonText}
|
|
207
|
+
labelStyle={styles.buttonLabel}
|
|
208
|
+
accessibilityLabel={t('canteen:allergeneTitle')}
|
|
209
|
+
onPress={() => setAdditionalDialogVisible(true)}
|
|
210
|
+
>
|
|
211
|
+
<IconsOpenasist
|
|
212
|
+
icon={"info"}
|
|
213
|
+
color={colors.secondaryText}
|
|
214
|
+
size={15}
|
|
215
|
+
/>
|
|
216
|
+
</Button>
|
|
217
|
+
<AdditionalDialog
|
|
218
|
+
allergens={meal?.allergens}
|
|
219
|
+
additives={meal?.additives}
|
|
220
|
+
visible={additionalDialogVisible}
|
|
221
|
+
onDismiss={() => setAdditionalDialogVisible(false)}
|
|
222
|
+
/>
|
|
223
|
+
</>
|
|
224
|
+
: null
|
|
225
|
+
}
|
|
226
|
+
</View>
|
|
227
|
+
</View>
|
|
228
|
+
</View>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function MensaMenu(props) {
|
|
233
|
+
const componentName = MensaMenu.name;
|
|
234
|
+
|
|
235
|
+
const { t } = useTranslation();
|
|
236
|
+
const { canteenId, menuDate, settings } = props;
|
|
237
|
+
const [menu, refreshMenu] = useFilteredMenu(canteenId, menuDate);
|
|
238
|
+
const theme = useTheme();
|
|
239
|
+
const { themeStyles, appSettings } = theme;
|
|
240
|
+
const availablePriceGroups = appSettings.groupsOfPersons;
|
|
241
|
+
const favoritePriceGroupCode = settings.settingsCanteens.favoritePrice;
|
|
242
|
+
const favoritePriceGroup = favoritePriceGroupCode
|
|
243
|
+
? availablePriceGroups.find(priceGroup => priceGroup.code === favoritePriceGroupCode)
|
|
244
|
+
: availablePriceGroups.find(priceGroup => priceGroup.default);
|
|
245
|
+
const favoritePriceGroupName = t(favoritePriceGroup.labelKey);
|
|
246
|
+
|
|
247
|
+
console.log(componentName, ':', 'favoritePriceGroupCode', ':', favoritePriceGroupCode);
|
|
248
|
+
console.log(componentName, ':', 'favoritePriceGroup', ':', favoritePriceGroup)
|
|
249
|
+
console.log(componentName, ':', 'favoritePriceGroupName', ':', favoritePriceGroupName)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
useEffect(
|
|
253
|
+
() => {
|
|
254
|
+
refreshMenu();
|
|
255
|
+
},
|
|
256
|
+
[]
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
// Generieren der styles. Wird nur genieret wenn nicht vorhanden oder die Property theme sich ändert.
|
|
260
|
+
const styles = useMemo(
|
|
261
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
262
|
+
[theme]
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
/*
|
|
266
|
+
Every single menu-Component is rendered by the Meal-function above with props(themeStyles, settings, etc.), the current item and the local defined styles
|
|
267
|
+
as attributes
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<View style={themeStyles.container}>
|
|
272
|
+
{
|
|
273
|
+
Array.isArray(menu) && menu.length > 0
|
|
274
|
+
? menu.flatMap(
|
|
275
|
+
(meal) => [
|
|
276
|
+
<Meal
|
|
277
|
+
key={meal.title}
|
|
278
|
+
meal={meal}
|
|
279
|
+
priceGroupCode={favoritePriceGroup.code}
|
|
280
|
+
priceGroupName={favoritePriceGroupName}
|
|
281
|
+
settings={settings}
|
|
282
|
+
/>,
|
|
283
|
+
<View
|
|
284
|
+
key={`seperator-${meal.title}`}
|
|
285
|
+
style={styles.listSeperator}
|
|
286
|
+
/>
|
|
287
|
+
]
|
|
288
|
+
)
|
|
289
|
+
: <View style={themeStyles.noticeTextContainer}>
|
|
290
|
+
<Text style={themeStyles.noticeText}>
|
|
291
|
+
{t('canteen:noMealAvailable')}
|
|
292
|
+
</Text>
|
|
293
|
+
</View>
|
|
294
|
+
}
|
|
295
|
+
</View>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const mapStateToProps = state => {
|
|
300
|
+
return {
|
|
301
|
+
pluginComponent: state.pluginReducer.mealItem.component,
|
|
302
|
+
pluginStyles: state.pluginReducer.mealItem.styles,
|
|
303
|
+
settings: state.settingReducer
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
export default connect(mapStateToProps, { onUpdateRefreshing })(MensaMenu)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export default function(theme) {
|
|
2
|
+
return {
|
|
3
|
+
container: {
|
|
4
|
+
backgroundColor: theme.colors.white,
|
|
5
|
+
flex: 1
|
|
6
|
+
},
|
|
7
|
+
innerContainer: {
|
|
8
|
+
flex: 1,
|
|
9
|
+
},
|
|
10
|
+
activity: {
|
|
11
|
+
margin: 40
|
|
12
|
+
},
|
|
13
|
+
mealDate: {
|
|
14
|
+
...theme.fonts.bold,
|
|
15
|
+
marginTop: theme.paddings.xsmall,
|
|
16
|
+
paddingLeft: theme.paddings.small,
|
|
17
|
+
},
|
|
18
|
+
mealContent: {
|
|
19
|
+
paddingTop: theme.paddings.default,
|
|
20
|
+
paddingLeft: theme.paddings.default,
|
|
21
|
+
paddingRight: theme.paddings.default,
|
|
22
|
+
marginBottom: theme.paddings.default
|
|
23
|
+
},
|
|
24
|
+
listSeperator: {
|
|
25
|
+
height: 1,
|
|
26
|
+
backgroundColor: theme.colors.listSeperator,
|
|
27
|
+
},
|
|
28
|
+
mealImage: {
|
|
29
|
+
aspectRatio: 16/9,
|
|
30
|
+
paddingBottom: theme.paddings.small,
|
|
31
|
+
},
|
|
32
|
+
mealItemTitle: {
|
|
33
|
+
flex: 1,
|
|
34
|
+
flexDirection: 'row',
|
|
35
|
+
justifyContent: 'flex-start',
|
|
36
|
+
alignItems: 'flex-end',
|
|
37
|
+
marginTop: theme.paddings.default,
|
|
38
|
+
marginBottom: theme.paddings.default,
|
|
39
|
+
},
|
|
40
|
+
mealItemTitleText: {
|
|
41
|
+
...theme.fonts.bold,
|
|
42
|
+
fontSize: theme.fontSizes.l,
|
|
43
|
+
lineHeight: theme.lineHeights.l,
|
|
44
|
+
},
|
|
45
|
+
mealItemTitleIcons: {
|
|
46
|
+
marginLeft: 5,
|
|
47
|
+
flex: 1,
|
|
48
|
+
flexDirection: 'row',
|
|
49
|
+
justifyContent: 'flex-start',
|
|
50
|
+
},
|
|
51
|
+
mealItemPrice: {
|
|
52
|
+
...theme.fonts.bold,
|
|
53
|
+
marginTop: theme.paddings.xsmall,
|
|
54
|
+
color: theme.colors.textAccent
|
|
55
|
+
},
|
|
56
|
+
mealItemText: {
|
|
57
|
+
...theme.fonts.regular,
|
|
58
|
+
fontSize: theme.fontSizes.l,
|
|
59
|
+
lineHeight: theme.lineHeights.l,
|
|
60
|
+
marginBottom: theme.paddings.default,
|
|
61
|
+
},
|
|
62
|
+
mealItemNoVisible: {
|
|
63
|
+
...theme.fonts.regular,
|
|
64
|
+
fontSize: theme.fontSizes.l,
|
|
65
|
+
lineHeight: theme.lineHeights.l,
|
|
66
|
+
marginBottom: theme.paddings.small,
|
|
67
|
+
color: theme.colors.noticeText,
|
|
68
|
+
},
|
|
69
|
+
buttonLabel: {
|
|
70
|
+
fontSize: theme.fontSizes.m
|
|
71
|
+
},
|
|
72
|
+
addionalInformation: {
|
|
73
|
+
flexDirection: 'row',
|
|
74
|
+
flexWrap: 'wrap',
|
|
75
|
+
justifyContent: 'space-between',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
},
|
|
78
|
+
addionals: {
|
|
79
|
+
display: 'flex',
|
|
80
|
+
justifyContent: 'space-between',
|
|
81
|
+
paddingBottom:theme.paddings.small,
|
|
82
|
+
paddingTop:theme.paddings.small,
|
|
83
|
+
},
|
|
84
|
+
dialogTitle: {
|
|
85
|
+
fontSize: theme.fontSizes.l,
|
|
86
|
+
lineHeight: theme.lineHeights.xxl
|
|
87
|
+
},
|
|
88
|
+
dialogContent: {
|
|
89
|
+
fontSize: theme.fontSizes.m,
|
|
90
|
+
color: theme.colors.text,
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
};
|
|
@@ -0,0 +1,184 @@
|
|
|
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, { useMemo, useEffect } from 'react';
|
|
16
|
+
|
|
17
|
+
import { ScrollView, StyleSheet, Text, View } from 'react-native';
|
|
18
|
+
import { connect } from 'react-redux'
|
|
19
|
+
import { useTheme } from 'react-native-paper';
|
|
20
|
+
import { useTranslation } from 'react-i18next';
|
|
21
|
+
import { LinearGradient } from 'expo-linear-gradient'
|
|
22
|
+
|
|
23
|
+
import { DateTime } from 'luxon';
|
|
24
|
+
|
|
25
|
+
import { MealItem as MealItemComponent } from '@olea-bps/components';
|
|
26
|
+
import { toIsoDateString } from '@olea-bps/core/helper/date';
|
|
27
|
+
|
|
28
|
+
import { useCanteen, useFilteredMenu, useFavoriteCanteens } from '@olea-bps/context-canteen';
|
|
29
|
+
|
|
30
|
+
import componentStyles from './styles'
|
|
31
|
+
|
|
32
|
+
function HorizontalMenu({ canteenId, menuDate }) {
|
|
33
|
+
const theme = useTheme();
|
|
34
|
+
const { t } = useTranslation();
|
|
35
|
+
|
|
36
|
+
const styles = useMemo(
|
|
37
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
38
|
+
[theme]
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const [canteen] = useCanteen(canteenId, menuDate);
|
|
42
|
+
const [menu, refreshMenu, mealAmount, filteredMealAmount] = useFilteredMenu(canteenId, menuDate);
|
|
43
|
+
|
|
44
|
+
useEffect(
|
|
45
|
+
() => {
|
|
46
|
+
refreshMenu();
|
|
47
|
+
},
|
|
48
|
+
[canteenId, menuDate]
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if (Array.isArray(menu) && menu.length > 0) {
|
|
52
|
+
return menu.map(
|
|
53
|
+
(meal, index) => {
|
|
54
|
+
return (
|
|
55
|
+
<View
|
|
56
|
+
key={`${canteenId}-${menuDate}-${meal.title}`}
|
|
57
|
+
>
|
|
58
|
+
<Text
|
|
59
|
+
style={styles.mensaNameText}
|
|
60
|
+
>
|
|
61
|
+
{
|
|
62
|
+
index === 0
|
|
63
|
+
? `${canteen.title} ${filteredMealAmount !== mealAmount ? `(${filteredMealAmount}/${mealAmount})` : ''}`
|
|
64
|
+
: null
|
|
65
|
+
}
|
|
66
|
+
</Text>
|
|
67
|
+
<MealItemComponent
|
|
68
|
+
meal={meal}
|
|
69
|
+
canteenId={canteenId}
|
|
70
|
+
menuDate={menuDate}
|
|
71
|
+
/>
|
|
72
|
+
</View>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
} else {
|
|
77
|
+
return (
|
|
78
|
+
<View
|
|
79
|
+
key={canteenId}
|
|
80
|
+
>
|
|
81
|
+
<Text
|
|
82
|
+
style={styles.mensaNameText}
|
|
83
|
+
>
|
|
84
|
+
{canteen.title}
|
|
85
|
+
</Text>
|
|
86
|
+
<Text
|
|
87
|
+
style={styles.mensaNameText}
|
|
88
|
+
>
|
|
89
|
+
{t(mealAmount ? 'canteen:noMeals' : 'canteen:closed')}
|
|
90
|
+
</Text>
|
|
91
|
+
</View>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Renders a list of meals for a specific canteen in a horizontal slider
|
|
97
|
+
function HorizontalCanteens({ menusDate, settings }) {
|
|
98
|
+
const { t } = useTranslation();
|
|
99
|
+
const theme = useTheme();
|
|
100
|
+
const styles = useMemo(
|
|
101
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
102
|
+
[theme]
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Favorisierte Mensen aus dem Mensa-Kontext holen
|
|
106
|
+
const [favoriteCanteens] = useFavoriteCanteens();
|
|
107
|
+
|
|
108
|
+
if (Array.isArray(favoriteCanteens) && favoriteCanteens.length > 0) {
|
|
109
|
+
return favoriteCanteens.map(
|
|
110
|
+
canteenId => <HorizontalMenu
|
|
111
|
+
key={`${canteenId}-${menusDate}`}
|
|
112
|
+
canteenId={canteenId}
|
|
113
|
+
menuDate={menusDate}
|
|
114
|
+
/>
|
|
115
|
+
)
|
|
116
|
+
} else {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function MensaSliderComponent(props) {
|
|
122
|
+
const { settings } = props;
|
|
123
|
+
|
|
124
|
+
const theme = useTheme();
|
|
125
|
+
const colors = theme?.colors;
|
|
126
|
+
const { t } = useTranslation();
|
|
127
|
+
|
|
128
|
+
const styles = useMemo(
|
|
129
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
130
|
+
[theme]
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const mensaShadow = colors.mensaShadow.filter((element, pos) => {
|
|
134
|
+
return colors.mensaShadow.indexOf(element) === pos;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const todayDateTime = new Date();
|
|
138
|
+
const todayDateString = toIsoDateString(todayDateTime);
|
|
139
|
+
|
|
140
|
+
const formattedDate = DateTime
|
|
141
|
+
.fromJSDate(todayDateTime)
|
|
142
|
+
.setLocale(settings.settingsGeneral.language)
|
|
143
|
+
.toLocaleString(DateTime.DATE_SHORT);
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<View style={styles.container}>
|
|
147
|
+
<Text style={styles.headline}>
|
|
148
|
+
{t('canteen:news')} - {formattedDate}
|
|
149
|
+
</Text>
|
|
150
|
+
<View style={styles.innerContainer}>
|
|
151
|
+
<LinearGradient
|
|
152
|
+
start={{ x: 0, y: 0 }}
|
|
153
|
+
end={{ x: 1, y: 0 }}
|
|
154
|
+
colors={mensaShadow}
|
|
155
|
+
style={styles.shadowLeft}
|
|
156
|
+
/>
|
|
157
|
+
<ScrollView
|
|
158
|
+
horizontal={true}
|
|
159
|
+
contentContainerStyle={styles.slider}
|
|
160
|
+
>
|
|
161
|
+
<HorizontalCanteens
|
|
162
|
+
{...props}
|
|
163
|
+
menusDate={todayDateString}
|
|
164
|
+
/>
|
|
165
|
+
</ScrollView>
|
|
166
|
+
<LinearGradient
|
|
167
|
+
start={{ x: 1, y: 0 }}
|
|
168
|
+
end={{ x: 0, y: 0 }}
|
|
169
|
+
colors={mensaShadow}
|
|
170
|
+
style={styles.shadowRight}
|
|
171
|
+
/>
|
|
172
|
+
</View>
|
|
173
|
+
</View>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const mapStateToProps = state => {
|
|
178
|
+
return {
|
|
179
|
+
canteens: state.apiReducer.canteens,
|
|
180
|
+
settings: state.settingReducer
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export default connect(mapStateToProps, null)(MensaSliderComponent);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export default function(theme) {
|
|
2
|
+
return {
|
|
3
|
+
container: {
|
|
4
|
+
paddingHorizontal: 0,
|
|
5
|
+
paddingTop: theme.paddings.small,
|
|
6
|
+
backgroundColor: theme.colors.mensaSliderBackground
|
|
7
|
+
},
|
|
8
|
+
innerContainer: {
|
|
9
|
+
flex: 1,
|
|
10
|
+
flexDirection: 'row',
|
|
11
|
+
position: 'relative'
|
|
12
|
+
},
|
|
13
|
+
mensaNameText: {
|
|
14
|
+
...theme.fonts.regular,
|
|
15
|
+
fontSize: theme.fontSizes.s,
|
|
16
|
+
lineHeight: theme.lineHeights.xs,
|
|
17
|
+
paddingBottom: theme.paddings.small,
|
|
18
|
+
marginLeft: theme.paddings.default,
|
|
19
|
+
color: theme.colors.mensaSliderTextColor,
|
|
20
|
+
textTransform: 'uppercase',
|
|
21
|
+
|
|
22
|
+
},
|
|
23
|
+
headline: {
|
|
24
|
+
paddingHorizontal: theme.paddings.default,
|
|
25
|
+
paddingBottom: theme.paddings.default,
|
|
26
|
+
...theme.fonts.regular,
|
|
27
|
+
fontSize: theme.fontSizes.m,
|
|
28
|
+
color: theme.colors.mensaSliderTextColor,
|
|
29
|
+
textTransform: 'uppercase',
|
|
30
|
+
|
|
31
|
+
},
|
|
32
|
+
slider: {
|
|
33
|
+
paddingBottom: theme.paddings.small,
|
|
34
|
+
zIndex: 1
|
|
35
|
+
},
|
|
36
|
+
shadowLeft: {
|
|
37
|
+
width: theme.paddings.default,
|
|
38
|
+
position: 'absolute',
|
|
39
|
+
top:0,
|
|
40
|
+
left: 0,
|
|
41
|
+
bottom: 0,
|
|
42
|
+
zIndex: 2
|
|
43
|
+
},
|
|
44
|
+
shadowRight: {
|
|
45
|
+
width: theme.paddings.default,
|
|
46
|
+
position: 'absolute',
|
|
47
|
+
top:0,
|
|
48
|
+
right: 0,
|
|
49
|
+
bottom: 0,
|
|
50
|
+
zIndex: 2
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|