@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,357 @@
|
|
|
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 from 'react';
|
|
16
|
+
import PropTypes from 'prop-types';
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
ScrollView, StyleSheet, Text,
|
|
20
|
+
View, SafeAreaView, Share, Linking
|
|
21
|
+
} from 'react-native';
|
|
22
|
+
import {Appbar, Headline, List, withTheme} from "react-native-paper";
|
|
23
|
+
|
|
24
|
+
import { connect } from 'react-redux'
|
|
25
|
+
import merge from 'lodash/merge';
|
|
26
|
+
|
|
27
|
+
import componentStyles from "./styles"
|
|
28
|
+
import { AppBar as AppbarComponent } from '@olea-bps/components';
|
|
29
|
+
import IconsOpenasist from "@olea-bps/icons-openasist";
|
|
30
|
+
import {withTranslation} from "react-i18next";
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Course Detail Component
|
|
35
|
+
*
|
|
36
|
+
* Shows the detailed information of a course with room, time, lecturer and so on.
|
|
37
|
+
* Provides a share functionality.
|
|
38
|
+
*
|
|
39
|
+
* Parameters:
|
|
40
|
+
* - course: Course object with all information about the course
|
|
41
|
+
*
|
|
42
|
+
* Navigation-Parameters:
|
|
43
|
+
* - none
|
|
44
|
+
*/
|
|
45
|
+
class CourseDetailComponent extends React.Component {
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// Styles of this component
|
|
49
|
+
styles;
|
|
50
|
+
|
|
51
|
+
course = null;
|
|
52
|
+
|
|
53
|
+
constructor(props) {
|
|
54
|
+
super(props);
|
|
55
|
+
|
|
56
|
+
// ------------------------------------------------------------------------
|
|
57
|
+
// PLUGIN FUNCTIONALITY
|
|
58
|
+
// ------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
const { pluginStyles,theme } = this.props;
|
|
61
|
+
this.styles = componentStyles(theme);
|
|
62
|
+
|
|
63
|
+
if(pluginStyles) {
|
|
64
|
+
this.styles = merge(componentStyles, pluginStyles);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.styles = StyleSheet.create(this.styles);
|
|
68
|
+
|
|
69
|
+
// ------------------------------------------------------------------------
|
|
70
|
+
this.course = this.props.route.params.course;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Share function for book
|
|
75
|
+
*
|
|
76
|
+
* @returns {Promise<void>}
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
async _onShare() {
|
|
80
|
+
try {
|
|
81
|
+
const { t } = this.props;
|
|
82
|
+
let keys = this._getSortedKeys();
|
|
83
|
+
let message = '';
|
|
84
|
+
|
|
85
|
+
keys.forEach(key => {
|
|
86
|
+
let item = this.course[key];
|
|
87
|
+
|
|
88
|
+
switch(key) {
|
|
89
|
+
case 'title':
|
|
90
|
+
message += item.data + '\n\n';
|
|
91
|
+
case 'lecturer':
|
|
92
|
+
if(item && item.length > 0) {
|
|
93
|
+
let title = '';
|
|
94
|
+
let content = '';
|
|
95
|
+
item.forEach(lecturerItem => {
|
|
96
|
+
title = lecturerItem.displayname;
|
|
97
|
+
content += lecturerItem.data + '\n';
|
|
98
|
+
});
|
|
99
|
+
message += title + ': \n' + content + '\n';
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
case 'times':
|
|
103
|
+
if(item && item.length > 0) {
|
|
104
|
+
let title = '';
|
|
105
|
+
let times = '';
|
|
106
|
+
item.forEach(timeItem => {
|
|
107
|
+
title = timeItem.displayname;
|
|
108
|
+
times += timeItem.start + ' - ' + timeItem.end + ' ' + t('common:time') +'\n';
|
|
109
|
+
});
|
|
110
|
+
message += title + ': \n' + times + '\n';
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case 'room':
|
|
114
|
+
if(item && item.length > 0) {
|
|
115
|
+
const showRoomNumbers = item.length > 1;
|
|
116
|
+
let rooms = '';
|
|
117
|
+
item.forEach((roomItem, i) => {
|
|
118
|
+
let roomNumber = (showRoomNumbers) ? (i+1) +'. ' : '';
|
|
119
|
+
rooms += roomNumber +
|
|
120
|
+
roomItem.displayname +
|
|
121
|
+
': \n' +
|
|
122
|
+
roomItem.data +
|
|
123
|
+
' (' + roomItem.url + ')' +
|
|
124
|
+
'\n';
|
|
125
|
+
});
|
|
126
|
+
message += rooms + '\n'
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
default:
|
|
130
|
+
if(item && item.displayname && item.data)
|
|
131
|
+
message += item.displayname + ': \n' + item.data + '\n\n';
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const result = await Share.share({
|
|
137
|
+
message: message
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (result.action === Share.sharedAction) {
|
|
141
|
+
if (result.activityType) {
|
|
142
|
+
// shared with activity type of result.activityType
|
|
143
|
+
} else {
|
|
144
|
+
// shared
|
|
145
|
+
}
|
|
146
|
+
} else if (result.action === Share.dismissedAction) {
|
|
147
|
+
// dismissed
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
alert(error.message);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Sort course keys by list and alphabetically
|
|
156
|
+
*
|
|
157
|
+
* @returns {string[]}
|
|
158
|
+
* @private
|
|
159
|
+
*/
|
|
160
|
+
_getSortedKeys() {
|
|
161
|
+
if(!this.course) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let sortOrder = ['title', 'type', 'times', 'room', 'lecturer'];
|
|
166
|
+
return Object.keys(this.course).sort((a,b) => {
|
|
167
|
+
// First sort by our defined order
|
|
168
|
+
if(sortOrder.indexOf(a) >= 0 && sortOrder.indexOf(b) >= 0)
|
|
169
|
+
return sortOrder.indexOf(a) - sortOrder.indexOf(b);
|
|
170
|
+
|
|
171
|
+
// If only a is a key of our sort order, move it up
|
|
172
|
+
if(sortOrder.indexOf(a) >= 0)
|
|
173
|
+
return -1;
|
|
174
|
+
|
|
175
|
+
// If a nor b is within the sort order, sort alphabetically
|
|
176
|
+
if(sortOrder.indexOf(b) === -1)
|
|
177
|
+
return a - b;
|
|
178
|
+
|
|
179
|
+
return 0;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Render _renderItems
|
|
185
|
+
*
|
|
186
|
+
* @returns {*}
|
|
187
|
+
* @private
|
|
188
|
+
*/
|
|
189
|
+
_renderItems = () => {
|
|
190
|
+
if(!this.course) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const {colors} = this.props.theme;
|
|
194
|
+
const {t} = this.props;
|
|
195
|
+
|
|
196
|
+
let keys = this._getSortedKeys();
|
|
197
|
+
let itemList = [];
|
|
198
|
+
|
|
199
|
+
keys.forEach(key => {
|
|
200
|
+
if(key === 'title') return;
|
|
201
|
+
let item = this.course[key];
|
|
202
|
+
|
|
203
|
+
switch(key) {
|
|
204
|
+
case 'lecturer':
|
|
205
|
+
if(item && item.length > 0) {
|
|
206
|
+
item.forEach(lecturerItem => {
|
|
207
|
+
itemList.push(
|
|
208
|
+
<List.Item key={key} title={lecturerItem.displayname ? lecturerItem.displayname : t('course:lecturer')}
|
|
209
|
+
description={lecturerItem.data}
|
|
210
|
+
titleStyle={this.styles.listTitle}
|
|
211
|
+
descriptionStyle={this.styles.listDescription}
|
|
212
|
+
descriptionNumberOfLines={200}
|
|
213
|
+
/>);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
case 'times':
|
|
218
|
+
if(item && item.length > 0) {
|
|
219
|
+
let title = '';
|
|
220
|
+
let times = '';
|
|
221
|
+
item.forEach(timeItem => {
|
|
222
|
+
title = timeItem.displayname;
|
|
223
|
+
times += timeItem.start + ' - ' + timeItem.end + ' ' + t('common:time') + ' - ' + timeItem.mode + '\n';
|
|
224
|
+
});
|
|
225
|
+
itemList.push(
|
|
226
|
+
<List.Item key={key} title={title}
|
|
227
|
+
description={times}
|
|
228
|
+
titleStyle={this.styles.listTitle}
|
|
229
|
+
descriptionStyle={this.styles.listDescription}
|
|
230
|
+
descriptionNumberOfLines={200} />);
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
case 'room':
|
|
234
|
+
if(item && item.length > 0) {
|
|
235
|
+
const showRoomNumbers = item.length > 1;
|
|
236
|
+
item.forEach((roomItem, i) => {
|
|
237
|
+
let roomNumber = (showRoomNumbers) ? (i+1) +'. ' : '';
|
|
238
|
+
itemList.push(
|
|
239
|
+
<List.Item
|
|
240
|
+
key={key + '_' + i}
|
|
241
|
+
title={roomNumber + roomItem.displayname}
|
|
242
|
+
description={roomItem.data}
|
|
243
|
+
titleStyle={this.styles.listTitle}
|
|
244
|
+
descriptionStyle={this.styles.listDescription}
|
|
245
|
+
right={props =>
|
|
246
|
+
<List.Icon {...props}
|
|
247
|
+
icon={props => <IconsOpenasist icon={"map-search"} size={25}
|
|
248
|
+
color={colors.icon}/>}/>
|
|
249
|
+
}
|
|
250
|
+
onPress={() => Linking.openURL(roomItem.url)}
|
|
251
|
+
/>);
|
|
252
|
+
});
|
|
253
|
+
} else if(item.data) {
|
|
254
|
+
itemList.push(
|
|
255
|
+
<List.Item
|
|
256
|
+
key={key}
|
|
257
|
+
title={t('room:title')}
|
|
258
|
+
description={item.data}
|
|
259
|
+
titleStyle={this.styles.listTitle}
|
|
260
|
+
descriptionStyle={this.styles.listDescription}
|
|
261
|
+
right={props =>
|
|
262
|
+
item.url ? (
|
|
263
|
+
<List.Icon {...props}
|
|
264
|
+
icon={props => <IconsOpenasist icon={"map-search"} size={25}
|
|
265
|
+
color={colors.icon}/>}/>
|
|
266
|
+
) : null
|
|
267
|
+
}
|
|
268
|
+
disabled={!item.url}
|
|
269
|
+
onPress={() => item.url && Linking.openURL(item.url)}
|
|
270
|
+
/>);
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
if(item && item.displayname && item.data)
|
|
275
|
+
itemList.push(
|
|
276
|
+
<List.Item key={key} title={item.displayname}
|
|
277
|
+
description={item.data}
|
|
278
|
+
titleStyle={this.styles.listTitle}
|
|
279
|
+
descriptionStyle={this.styles.listDescription}
|
|
280
|
+
descriptionNumberOfLines={200}
|
|
281
|
+
onPress={() => item.url && Linking.openURL(item.url)}
|
|
282
|
+
right={
|
|
283
|
+
props => item.url && <List.Icon {...props}
|
|
284
|
+
icon={props => <IconsOpenasist
|
|
285
|
+
icon={"forward"} size={25}
|
|
286
|
+
color={colors.icon}/>}/>}
|
|
287
|
+
/>);
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
<>
|
|
294
|
+
{itemList}
|
|
295
|
+
</>
|
|
296
|
+
)
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Render book details content
|
|
301
|
+
*
|
|
302
|
+
* @returns {*}
|
|
303
|
+
* @private
|
|
304
|
+
*/
|
|
305
|
+
_renderContent = () => {
|
|
306
|
+
const {themeStyles} = this.props.theme;
|
|
307
|
+
return (
|
|
308
|
+
<View style={[themeStyles.container, this.styles.containerInner]}>
|
|
309
|
+
<Headline style={this.styles.titleHeadline}>{this.course.title.data}</Headline>
|
|
310
|
+
<ScrollView>
|
|
311
|
+
{this._renderItems()}
|
|
312
|
+
</ScrollView>
|
|
313
|
+
</View>
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
render() {
|
|
318
|
+
// ------------------------------------------------------------------------
|
|
319
|
+
// PLUGIN FUNCTIONALITY
|
|
320
|
+
// ------------------------------------------------------------------------
|
|
321
|
+
const PluginComponent = this.props.pluginComponent;
|
|
322
|
+
if (PluginComponent) {
|
|
323
|
+
return <PluginComponent />;
|
|
324
|
+
}
|
|
325
|
+
// ------------------------------------------------------------------------
|
|
326
|
+
|
|
327
|
+
const {themeStyles} = this.props.theme;
|
|
328
|
+
const { course, t } = this.props;
|
|
329
|
+
|
|
330
|
+
if(!this.course){
|
|
331
|
+
return (
|
|
332
|
+
<SafeAreaView style={[this.styles.container, themeStyles.appSafeAreaContainer]}>
|
|
333
|
+
<AppbarComponent {...this.props}
|
|
334
|
+
title={t('course:lectureInformation')}/>
|
|
335
|
+
<Text>{t('course:couldNotLoad')}</Text>
|
|
336
|
+
</SafeAreaView>
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return (
|
|
341
|
+
<SafeAreaView style={[this.styles.container, themeStyles.appSafeAreaContainer]}>
|
|
342
|
+
<AppbarComponent {...this.props}
|
|
343
|
+
title={t('course:lectureInformation')}
|
|
344
|
+
rightAction={<Appbar.Action icon="share-variant" onPress={this._onShare.bind(this)}/>}/>
|
|
345
|
+
{this._renderContent()}
|
|
346
|
+
</SafeAreaView>
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
const mapStateToProps = state => {
|
|
353
|
+
return {
|
|
354
|
+
};
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
export default connect(mapStateToProps, null)(withTranslation()(withTheme(CourseDetailComponent)))
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export default function(theme) {
|
|
2
|
+
return {
|
|
3
|
+
container: {
|
|
4
|
+
flex: 1,
|
|
5
|
+
backgroundColor: theme.colors.contentBackground,
|
|
6
|
+
},
|
|
7
|
+
containerInner: {
|
|
8
|
+
flex: 1,
|
|
9
|
+
paddingHorizontal: 20,
|
|
10
|
+
paddingVertical: 20
|
|
11
|
+
},
|
|
12
|
+
activity: {
|
|
13
|
+
marginTop: 20
|
|
14
|
+
},
|
|
15
|
+
listTitle: {
|
|
16
|
+
...theme.fonts.regular,
|
|
17
|
+
fontSize: theme.fontSizes.m,
|
|
18
|
+
lineHeight: theme.lineHeights.xs
|
|
19
|
+
},
|
|
20
|
+
listDescription: {
|
|
21
|
+
...theme.fonts.regular,
|
|
22
|
+
fontSize: theme.fontSizes.s,
|
|
23
|
+
lineHeight: theme.lineHeights.xxs,
|
|
24
|
+
color: theme.colors.categoryBadgeColor,
|
|
25
|
+
},
|
|
26
|
+
titleHeadline : {
|
|
27
|
+
...theme.fonts.medium,
|
|
28
|
+
fontSize: theme.fontSizes.xl,
|
|
29
|
+
lineHeight: theme.lineHeights.l,
|
|
30
|
+
marginBottom: theme.paddings.small,
|
|
31
|
+
paddingHorizontal: 15
|
|
32
|
+
},
|
|
33
|
+
buttonText: {
|
|
34
|
+
...theme.fonts.medium,
|
|
35
|
+
color: theme.colors.messages.noticeText,
|
|
36
|
+
marginLeft: theme.paddings.small
|
|
37
|
+
},
|
|
38
|
+
buttonHeadline: {
|
|
39
|
+
fontSize: theme.fontSizes.xl,
|
|
40
|
+
lineHeight: theme.lineHeights.l
|
|
41
|
+
},
|
|
42
|
+
buttonContainer: {
|
|
43
|
+
flex: 1,
|
|
44
|
+
justifyContent: 'flex-end',
|
|
45
|
+
marginBottom: theme.paddings.large + theme.paddings.default
|
|
46
|
+
},
|
|
47
|
+
button: {
|
|
48
|
+
flexDirection: 'row',
|
|
49
|
+
flexWrap: 'wrap',
|
|
50
|
+
justifyContent: 'center',
|
|
51
|
+
alignItems: 'center',
|
|
52
|
+
padding: theme.paddings.small,
|
|
53
|
+
},
|
|
54
|
+
campusFinderUrl: {
|
|
55
|
+
backgroundColor: theme.colors.primary,
|
|
56
|
+
marginBottom: theme.paddings.small + theme.paddings.xsmall
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
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 } from 'react';
|
|
16
|
+
import {
|
|
17
|
+
StyleSheet,
|
|
18
|
+
Linking,
|
|
19
|
+
TouchableOpacity
|
|
20
|
+
} from 'react-native';
|
|
21
|
+
|
|
22
|
+
import { useTheme, Text, Dialog, Portal } from 'react-native-paper';
|
|
23
|
+
import { useTranslation } from 'react-i18next';
|
|
24
|
+
|
|
25
|
+
import IconsOpenasist from '@olea-bps/icons-openasist';
|
|
26
|
+
import { useIncreaseFontSize } from '@olea-bps/core';
|
|
27
|
+
|
|
28
|
+
import componentStyles from './styles';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Ein Dialog, der sich über die derzeitige Ansicht legt, welcher die Detailinformationen zu einer Vorlesung anzeigt.
|
|
32
|
+
*
|
|
33
|
+
* @param {object} props
|
|
34
|
+
* @param {object} props.course Das JSON-Objekt, welches die Daten der Vorlesung enthält
|
|
35
|
+
* @param {boolean} props.visible `true`, wenn der Dialog angezeigt werden soll, ansonsten `false`
|
|
36
|
+
* @param {() => void} [props.onClose]
|
|
37
|
+
* @param {() => void} [props.onDismiss]
|
|
38
|
+
* @example
|
|
39
|
+
* const [showDetailDialog, setShowDetailDialog] = useState(true);
|
|
40
|
+
*
|
|
41
|
+
* const course = {
|
|
42
|
+
* title: { displayName: 'Bezeichnung', data: 'Programierung' },
|
|
43
|
+
* room: { displayName: 'Raum', data: '13.344.3' },
|
|
44
|
+
* startTime: { displayName: 'Begin', data: '12:00' }
|
|
45
|
+
* };
|
|
46
|
+
*
|
|
47
|
+
* return (
|
|
48
|
+
* <CourseDetailDialog
|
|
49
|
+
* course={course}
|
|
50
|
+
* visible={showDetailDialog}
|
|
51
|
+
* onDismiss={() => setShowDetailDialog(false)}
|
|
52
|
+
* onClose={() => setShowDetailDialog(false)}
|
|
53
|
+
* />
|
|
54
|
+
* );
|
|
55
|
+
*/
|
|
56
|
+
export default function CourseDetailDialog({ course, visible, onClose, onDismiss }) {
|
|
57
|
+
const theme = useTheme();
|
|
58
|
+
const { t } = useTranslation();
|
|
59
|
+
|
|
60
|
+
const styles = useMemo(
|
|
61
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
62
|
+
[theme]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const { room, url, info, title, times, type, lecturer, ...extraCourseData } = course ?? {};
|
|
66
|
+
const courseDetails = [
|
|
67
|
+
{ ...room, key: 'room', displayName: t('timetable:detail.room'), icon: 'map-search' },
|
|
68
|
+
{ key: 'startTimes', data: times?.[0]?.start, displayName: t('timetable:detail.start') },
|
|
69
|
+
{ key: 'endTimes', data: times?.[0]?.end, displayName: t('timetable:detail.end') },
|
|
70
|
+
{ ...type, key: 'type', displayName: t('timetable:detail.type') },
|
|
71
|
+
{ ...lecturer?.[0], key: 'lecturer', displayName: t('timetable:detail.lecturer') },
|
|
72
|
+
{ ...url, key: 'url', displayName: t('timetable:detail.url'), icon: 'forward' },
|
|
73
|
+
{ ...info, key: 'info', displayName: t('timetable:detail.info') },
|
|
74
|
+
|
|
75
|
+
// Restliche Daten werden in Format gebracht und angefügt
|
|
76
|
+
...Object.entries(extraCourseData)
|
|
77
|
+
.map(
|
|
78
|
+
([courseDataKey, courseDataValue]) =>
|
|
79
|
+
(
|
|
80
|
+
{
|
|
81
|
+
key: courseDataKey,
|
|
82
|
+
displayName: courseDataValue?.displayname,
|
|
83
|
+
...courseDataValue,
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
]
|
|
88
|
+
.filter(courseDetail => courseDetail?.data ? true : false);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<Portal>
|
|
92
|
+
<Dialog visible={visible} onDismiss={onDismiss}>
|
|
93
|
+
<Dialog.Title>{title?.data ?? 'Keine weiteren Informationen'}</Dialog.Title>
|
|
94
|
+
<Dialog.Content>
|
|
95
|
+
{
|
|
96
|
+
courseDetails
|
|
97
|
+
? courseDetails.map(
|
|
98
|
+
courseDetail =>
|
|
99
|
+
<CourseDetailDialogText
|
|
100
|
+
key={courseDetail?.displayName}
|
|
101
|
+
title={courseDetail?.displayName}
|
|
102
|
+
content={courseDetail?.data}
|
|
103
|
+
url={courseDetail?.url}
|
|
104
|
+
icon={courseDetail?.icon}
|
|
105
|
+
/>
|
|
106
|
+
)
|
|
107
|
+
: <Text>
|
|
108
|
+
Keine Informationen verfügbar
|
|
109
|
+
</Text>
|
|
110
|
+
}
|
|
111
|
+
</Dialog.Content>
|
|
112
|
+
<Dialog.Actions>
|
|
113
|
+
<TouchableOpacity onPress={onClose}>
|
|
114
|
+
<Text style={styles.dismissButton}>{t('timetable:close')}</Text>
|
|
115
|
+
</TouchableOpacity>
|
|
116
|
+
</Dialog.Actions>
|
|
117
|
+
</Dialog>
|
|
118
|
+
</Portal>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Eine einzelne Information im {@link CourseDetailDialog}
|
|
124
|
+
*
|
|
125
|
+
* @param {object} props
|
|
126
|
+
* @param {string} props.title
|
|
127
|
+
* @param {string} props.content
|
|
128
|
+
* @param {string} props.url
|
|
129
|
+
* @param {string} props.icon
|
|
130
|
+
*/
|
|
131
|
+
function CourseDetailDialogText({ title, content, url, icon }) {
|
|
132
|
+
const increaseFontSize = useIncreaseFontSize();
|
|
133
|
+
|
|
134
|
+
const theme = useTheme();
|
|
135
|
+
const { colors } = theme;
|
|
136
|
+
|
|
137
|
+
const styles = useMemo(
|
|
138
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
139
|
+
[theme]
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<TouchableOpacity
|
|
144
|
+
style={[styles.detailContainer, styles.textDetail]}
|
|
145
|
+
onPress={
|
|
146
|
+
url
|
|
147
|
+
? () => Linking.openURL(url)
|
|
148
|
+
: null
|
|
149
|
+
}
|
|
150
|
+
disabled={
|
|
151
|
+
url
|
|
152
|
+
? false
|
|
153
|
+
: true
|
|
154
|
+
}
|
|
155
|
+
>
|
|
156
|
+
<Text style={[styles.detailLabel, increaseFontSize ? styles.textDetailBigFont : styles.textDetail]} >
|
|
157
|
+
{title}
|
|
158
|
+
</Text>
|
|
159
|
+
<Text style={[styles.detailValue, increaseFontSize ? styles.textDetailBigFont : styles.textDetail]} ellipsizeMode='tail' >
|
|
160
|
+
{content}
|
|
161
|
+
</Text>
|
|
162
|
+
{
|
|
163
|
+
icon && url
|
|
164
|
+
? <IconsOpenasist icon={icon} size={25} color={colors.icon} />
|
|
165
|
+
: null
|
|
166
|
+
}
|
|
167
|
+
</TouchableOpacity>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export default function(theme) {
|
|
2
|
+
return {
|
|
3
|
+
courseCard: {
|
|
4
|
+
paddingLeft: 5,
|
|
5
|
+
},
|
|
6
|
+
verticalSeperator: {
|
|
7
|
+
height: "100%",
|
|
8
|
+
marginLeft: theme.paddings.small,
|
|
9
|
+
width: 1,
|
|
10
|
+
backgroundColor: theme.colors.listSeperator
|
|
11
|
+
},
|
|
12
|
+
verticalSeperatorSmall: {
|
|
13
|
+
height: "15%",
|
|
14
|
+
width: 1,
|
|
15
|
+
backgroundColor: theme.colors.listSeperator,
|
|
16
|
+
},
|
|
17
|
+
courseTimeContainer: {
|
|
18
|
+
width:"15%",
|
|
19
|
+
alignItems: "center",
|
|
20
|
+
justifyContent: "center",
|
|
21
|
+
backgroundColor:theme.colors.background
|
|
22
|
+
},
|
|
23
|
+
courseTimeContainerBigFont: {
|
|
24
|
+
width: "25%",
|
|
25
|
+
},
|
|
26
|
+
timeText: {
|
|
27
|
+
fontSize: theme.fontSizes.subtitle,
|
|
28
|
+
paddingTop: theme.paddings.small,
|
|
29
|
+
paddingBottom: theme.paddings.small
|
|
30
|
+
},
|
|
31
|
+
timeTextBig: {
|
|
32
|
+
fontSize: theme.fontSizes.m
|
|
33
|
+
},
|
|
34
|
+
courseContainer: {
|
|
35
|
+
flex: 1,
|
|
36
|
+
flexDirection: "column",
|
|
37
|
+
paddingLeft: theme.paddings.small,
|
|
38
|
+
width:"85%",
|
|
39
|
+
backgroundColor:theme.colors.background,
|
|
40
|
+
paddingTop: theme.paddings.xsmall,
|
|
41
|
+
paddingBottom: theme.paddings.xsmall
|
|
42
|
+
},
|
|
43
|
+
courseContainerBigFont: {
|
|
44
|
+
width: "75%",
|
|
45
|
+
},
|
|
46
|
+
type: {
|
|
47
|
+
fontSize: theme.fontSizes.subtitle,
|
|
48
|
+
color: theme.colors.subtitle
|
|
49
|
+
},
|
|
50
|
+
title: {
|
|
51
|
+
fontSize: theme.fontSizes.xl,
|
|
52
|
+
paddingTop: theme.paddings.xsmall,
|
|
53
|
+
},
|
|
54
|
+
titleBigFont: {
|
|
55
|
+
fontSize: theme.fontSizes.xl,
|
|
56
|
+
paddingTop: theme.paddings.xsmall,
|
|
57
|
+
},
|
|
58
|
+
room: {
|
|
59
|
+
fontSize: theme.fontSizes.l,
|
|
60
|
+
paddingTop: theme.paddings.xsmall,
|
|
61
|
+
fontWeight: "bold",
|
|
62
|
+
},
|
|
63
|
+
addLeftRightPadding: {
|
|
64
|
+
paddingLeft: theme.paddings.xsmall,
|
|
65
|
+
paddingRight: theme.paddings.xsmall,
|
|
66
|
+
},
|
|
67
|
+
professorText: {
|
|
68
|
+
fontSize: theme.fontSizes.l,
|
|
69
|
+
paddingTop: theme.paddings.xsmall,
|
|
70
|
+
color: theme.colors.subtitle
|
|
71
|
+
},
|
|
72
|
+
professorName: {
|
|
73
|
+
color: theme.colors.lecturerNameText
|
|
74
|
+
},
|
|
75
|
+
otherCourseContainer:{
|
|
76
|
+
padding: theme.paddings.xsmall,
|
|
77
|
+
width: "100%",
|
|
78
|
+
backgroundColor: theme.colors.background
|
|
79
|
+
},
|
|
80
|
+
btnPosition: {
|
|
81
|
+
position: 'absolute',
|
|
82
|
+
bottom: 7,
|
|
83
|
+
right: 7
|
|
84
|
+
},
|
|
85
|
+
textDetail: {
|
|
86
|
+
fontSize: theme.fontSizes.l,
|
|
87
|
+
paddingTop: theme.paddings.small,
|
|
88
|
+
},
|
|
89
|
+
textDetailBigFont: {
|
|
90
|
+
fontSize: theme.fontSizes.xl,
|
|
91
|
+
paddingTop: theme.paddings.small,
|
|
92
|
+
},
|
|
93
|
+
indentedText: {
|
|
94
|
+
marginLeft: 10,
|
|
95
|
+
},
|
|
96
|
+
detailContainer: {
|
|
97
|
+
flexDirection: 'row',
|
|
98
|
+
alignItems: 'center',
|
|
99
|
+
},
|
|
100
|
+
detailLabel: {
|
|
101
|
+
width: '25%',
|
|
102
|
+
},
|
|
103
|
+
detailValue: {
|
|
104
|
+
flexShrink: 1,
|
|
105
|
+
width: '70%',
|
|
106
|
+
},
|
|
107
|
+
dismissButton: {
|
|
108
|
+
color: '#007bff',
|
|
109
|
+
textAlign: 'right',
|
|
110
|
+
fontWeight: 'bold',
|
|
111
|
+
fontSize: 16,
|
|
112
|
+
marginBottom: 8,
|
|
113
|
+
marginRight: 10
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
};
|