@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,245 @@
|
|
|
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 { useState, useMemo, useCallback } from 'react';
|
|
16
|
+
import { View, Text, TouchableOpacity, StyleSheet, useWindowDimensions } from 'react-native';
|
|
17
|
+
import { connect } from 'react-redux';
|
|
18
|
+
import { withTheme } from 'react-native-paper';
|
|
19
|
+
import { withTranslation } from 'react-i18next';
|
|
20
|
+
import { Calendar } from 'react-native-big-calendar';
|
|
21
|
+
import { useCourses } from '@olea-bps/context-timetable';
|
|
22
|
+
import CalendarStrip from 'react-native-calendar-strip';
|
|
23
|
+
import { DateTime, Duration } from 'luxon';
|
|
24
|
+
import { TabView } from 'react-native-tab-view';
|
|
25
|
+
import { onUpdateRefreshing } from '@olea-bps/core';
|
|
26
|
+
import { CourseDetailDialog } from '@olea-bps/components';
|
|
27
|
+
import moment from 'moment';
|
|
28
|
+
import 'moment/locale/de';
|
|
29
|
+
|
|
30
|
+
import componentStyles from './styles';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Die Zeitspanne, welche vor und nach heute angezeigt werden soll.
|
|
34
|
+
*/
|
|
35
|
+
const daysTabDuration = Duration.fromISO('P6M');
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
* @param {object} props
|
|
40
|
+
* @param {(DateTime)=> void} props.onWeekChanged
|
|
41
|
+
* @returns
|
|
42
|
+
*/
|
|
43
|
+
function CalendarWeek(props) {
|
|
44
|
+
const { selectedISOWeek, theme, settings, calendarScrollOffsetMinutes, onWeekChanged, onCourseSelected } = props;
|
|
45
|
+
|
|
46
|
+
const styles = useMemo(
|
|
47
|
+
() => StyleSheet.create(componentStyles(theme)),
|
|
48
|
+
[theme]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const { width } = useWindowDimensions();
|
|
52
|
+
|
|
53
|
+
const [courses] = useCourses();
|
|
54
|
+
|
|
55
|
+
const language = settings?.settingsGeneral?.language;
|
|
56
|
+
|
|
57
|
+
const selectedWeekDate = useMemo(
|
|
58
|
+
() => DateTime.fromISO(selectedISOWeek),
|
|
59
|
+
[selectedISOWeek]
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const now = DateTime.now()
|
|
63
|
+
const todayWeekNumber = now.weekNumber;
|
|
64
|
+
const todayWeekYearNumber = now.weekYear;
|
|
65
|
+
|
|
66
|
+
const weeksTabViewRoutes = useMemo(
|
|
67
|
+
() => {
|
|
68
|
+
const todayWeekYear = DateTime.fromObject({ weekNumber: todayWeekNumber, weekYear: todayWeekYearNumber }).startOf('week');
|
|
69
|
+
const beginWeekYear = todayWeekYear.minus(daysTabDuration).startOf('week');
|
|
70
|
+
const endWeekYear = todayWeekYear.plus(daysTabDuration).endOf('week');
|
|
71
|
+
let currentWeekYear = DateTime.fromObject(beginWeekYear.toObject());
|
|
72
|
+
|
|
73
|
+
const weekYearRoutes = [];
|
|
74
|
+
|
|
75
|
+
while (currentWeekYear <= endWeekYear) {
|
|
76
|
+
const currentISOWeekYear = currentWeekYear.toISOWeekDate();
|
|
77
|
+
|
|
78
|
+
const currentWeekYearBeginDate = currentWeekYear.startOf('week');
|
|
79
|
+
const currentWeekYearEndDate = currentWeekYear.endOf('week');
|
|
80
|
+
let currentWeekYearDate = DateTime.fromObject(currentWeekYearBeginDate.toObject());
|
|
81
|
+
|
|
82
|
+
const currentWeekYearCourses = [];
|
|
83
|
+
|
|
84
|
+
const eventColor = theme.colors.eventContainerSidebar;
|
|
85
|
+
|
|
86
|
+
while (currentWeekYearDate <= currentWeekYearEndDate) {
|
|
87
|
+
const currentWeekYearISODate = currentWeekYearDate.toISODate();
|
|
88
|
+
const currentWeekYearDateCourses = courses?.[currentWeekYearISODate]
|
|
89
|
+
?.map(
|
|
90
|
+
course => (
|
|
91
|
+
{
|
|
92
|
+
title: course.title.data,
|
|
93
|
+
start: course.startDateTime,
|
|
94
|
+
end: course.endDateTime,
|
|
95
|
+
color: eventColor,
|
|
96
|
+
type: course.type?.data,
|
|
97
|
+
professor: course.lecturer[0]?.data,
|
|
98
|
+
room: course.room.data,
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
) ?? [];
|
|
102
|
+
|
|
103
|
+
currentWeekYearCourses.push(currentWeekYearDateCourses);
|
|
104
|
+
|
|
105
|
+
currentWeekYearDate = currentWeekYearDate.plus({ day: 1 });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
weekYearRoutes.push(
|
|
109
|
+
{
|
|
110
|
+
key: currentISOWeekYear,
|
|
111
|
+
weekbeginISODate: currentWeekYearBeginDate.toISODate(),
|
|
112
|
+
week: currentWeekYear.weekNumber,
|
|
113
|
+
weekYear: currentWeekYear.weekYear,
|
|
114
|
+
events: currentWeekYearCourses.flat(),
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
currentWeekYear = currentWeekYear.plus({ week: 1 });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return weekYearRoutes;
|
|
122
|
+
},
|
|
123
|
+
[todayWeekNumber, todayWeekYearNumber, courses]
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const weeksTabViewIndexes = useMemo(
|
|
127
|
+
() => weeksTabViewRoutes
|
|
128
|
+
.reduce(
|
|
129
|
+
(accumulator, currentRoute, currentIndex) =>
|
|
130
|
+
(
|
|
131
|
+
{
|
|
132
|
+
...accumulator,
|
|
133
|
+
[currentRoute.key]: currentIndex
|
|
134
|
+
}
|
|
135
|
+
),
|
|
136
|
+
{}
|
|
137
|
+
),
|
|
138
|
+
[weeksTabViewRoutes]
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
const weeksTabViewIndex = weeksTabViewIndexes[selectedISOWeek];
|
|
142
|
+
|
|
143
|
+
const renderEvent = useCallback((event, touchableOpacityProps) => {
|
|
144
|
+
const { style, ...restTouchableOpacityProps } = touchableOpacityProps;
|
|
145
|
+
return (
|
|
146
|
+
<>
|
|
147
|
+
<TouchableOpacity
|
|
148
|
+
{...restTouchableOpacityProps}
|
|
149
|
+
style={[style, styles.eventContainer, { borderTopColor: event.color || '#000' }]}
|
|
150
|
+
>
|
|
151
|
+
<View style={styles.eventHeader}>
|
|
152
|
+
<Text style={styles.eventTitle} numberOfLines={2} ellipsizeMode={'clip'} >{event.title}</Text>
|
|
153
|
+
</View>
|
|
154
|
+
<Text style={styles.eventRoom} numberOfLines={1} ellipsizeMode={'clip'}>{event.room}</Text>
|
|
155
|
+
</TouchableOpacity>
|
|
156
|
+
</>
|
|
157
|
+
);
|
|
158
|
+
}, [styles]);
|
|
159
|
+
|
|
160
|
+
const renderScene = useCallback(
|
|
161
|
+
({ route }) => {
|
|
162
|
+
return (
|
|
163
|
+
<View style={{ flex: 1, width: width }}>
|
|
164
|
+
<Calendar
|
|
165
|
+
events={route.events}
|
|
166
|
+
renderEvent={renderEvent}
|
|
167
|
+
renderHeader={() => null}
|
|
168
|
+
mode={'week'}
|
|
169
|
+
height={800}
|
|
170
|
+
headerContentStyle={{ backgroundColor: 'transparent' }}
|
|
171
|
+
weekStartsOn={1}
|
|
172
|
+
date={route.weekbeginISODate}
|
|
173
|
+
onPressEvent={
|
|
174
|
+
(event) => {
|
|
175
|
+
const eventDateISO = DateTime.fromJSDate(event.start).toISODate();
|
|
176
|
+
|
|
177
|
+
const eventStartTime = event.start.getTime();
|
|
178
|
+
const originalCourse = courses[eventDateISO]
|
|
179
|
+
?.find(
|
|
180
|
+
course =>
|
|
181
|
+
eventStartTime === course.startDateTime.getTime()
|
|
182
|
+
&&
|
|
183
|
+
event?.title === course?.title?.data
|
|
184
|
+
&&
|
|
185
|
+
event?.professor === course?.lecturer[0]?.data
|
|
186
|
+
&&
|
|
187
|
+
event?.room === course?.room?.data
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
onCourseSelected(originalCourse);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
swipeEnabled={false}
|
|
194
|
+
scrollOffsetMinutes={calendarScrollOffsetMinutes}
|
|
195
|
+
hourStyle={styles.hourStyle}
|
|
196
|
+
/>
|
|
197
|
+
</View>
|
|
198
|
+
);
|
|
199
|
+
},
|
|
200
|
+
[styles, width, courses, renderEvent]
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<>
|
|
205
|
+
<View style={styles.viewStrip}>
|
|
206
|
+
<View pointerEvents="none">
|
|
207
|
+
<CalendarStrip
|
|
208
|
+
style={styles.strip}
|
|
209
|
+
locale={{
|
|
210
|
+
name: language,
|
|
211
|
+
config: moment.localeData(language)
|
|
212
|
+
}}
|
|
213
|
+
highlightDateContainerStyle={styles.highlightDateContainer}
|
|
214
|
+
showMonth={false}
|
|
215
|
+
iconStyle={{ display: 'none' }}
|
|
216
|
+
startingDate={selectedWeekDate.plus({ day: 1 }).toJSDate()}
|
|
217
|
+
pointerEvents={'none'}
|
|
218
|
+
/>
|
|
219
|
+
</View>
|
|
220
|
+
</View>
|
|
221
|
+
|
|
222
|
+
<TabView
|
|
223
|
+
navigationState={{ index: weeksTabViewIndex, routes: weeksTabViewRoutes }}
|
|
224
|
+
renderTabBar={() => null}
|
|
225
|
+
lazy
|
|
226
|
+
lazyPreloadDistance={2}
|
|
227
|
+
onIndexChange={
|
|
228
|
+
(index) => {
|
|
229
|
+
const deltaISOWeekYear = weeksTabViewRoutes?.[index]?.key;
|
|
230
|
+
onWeekChanged(deltaISOWeekYear);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
renderScene={renderScene}
|
|
234
|
+
/>
|
|
235
|
+
</>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const mapStateToProps = (state) => {
|
|
240
|
+
return {
|
|
241
|
+
settings: state.settingReducer,
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export default connect(mapStateToProps, { onUpdateRefreshing })(withTranslation()(withTheme(CalendarWeek)));
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export default function (theme) {
|
|
2
|
+
return {
|
|
3
|
+
eventContainer: {
|
|
4
|
+
borderTopWidth: 8,
|
|
5
|
+
paddingTop: 5,
|
|
6
|
+
paddingLeft: 5,
|
|
7
|
+
paddingBottom: 10,
|
|
8
|
+
marginBottom: 10,
|
|
9
|
+
backgroundColor: theme.colors.eventContainerBackground,
|
|
10
|
+
borderRadius: 8,
|
|
11
|
+
shadowColor: '#000',
|
|
12
|
+
shadowOffset: { width: 0, height: 2 },
|
|
13
|
+
shadowOpacity: 0.3,
|
|
14
|
+
shadowRadius: 3,
|
|
15
|
+
elevation: 5,
|
|
16
|
+
overflow: 'visible'
|
|
17
|
+
},
|
|
18
|
+
eventTitle: {
|
|
19
|
+
fontSize: 12,
|
|
20
|
+
fontWeight: 'bold',
|
|
21
|
+
},
|
|
22
|
+
eventRoom: {
|
|
23
|
+
fontSize: 11,
|
|
24
|
+
color: '#757575',
|
|
25
|
+
},
|
|
26
|
+
highlightDateContainer: {
|
|
27
|
+
borderRadius: 100,
|
|
28
|
+
borderWidth: 3,
|
|
29
|
+
borderColor: 'red',
|
|
30
|
+
height: 48,
|
|
31
|
+
width: 48,
|
|
32
|
+
},
|
|
33
|
+
line: {
|
|
34
|
+
backgroundColor: '#000',
|
|
35
|
+
},
|
|
36
|
+
strip: {
|
|
37
|
+
height: 65,
|
|
38
|
+
width: '89%',
|
|
39
|
+
paddingTop: 5,
|
|
40
|
+
paddingBottom: 5,
|
|
41
|
+
backgroundColor: '#fff',
|
|
42
|
+
alignSelf: 'flex-end'
|
|
43
|
+
},
|
|
44
|
+
viewStrip: {
|
|
45
|
+
height: 65,
|
|
46
|
+
backgroundColor: '#fff',
|
|
47
|
+
elevation: 4,
|
|
48
|
+
shadowColor: '#000',
|
|
49
|
+
shadowOffset: { width: 0, height: 3 },
|
|
50
|
+
shadowOpacity: 0.2,
|
|
51
|
+
shadowRadius: 2,
|
|
52
|
+
},
|
|
53
|
+
hourStyle: {
|
|
54
|
+
fontSize: 12,
|
|
55
|
+
color: theme.colors.text,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
package/TopNews/index.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
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 {
|
|
17
|
+
Animated,
|
|
18
|
+
Dimensions,
|
|
19
|
+
StyleSheet,
|
|
20
|
+
Text,
|
|
21
|
+
TouchableOpacity,
|
|
22
|
+
View
|
|
23
|
+
} from 'react-native';
|
|
24
|
+
|
|
25
|
+
import {connect} from 'react-redux';
|
|
26
|
+
import {withTheme} from "react-native-paper";
|
|
27
|
+
|
|
28
|
+
import merge from 'lodash/merge';
|
|
29
|
+
import NetInfo from '@react-native-community/netinfo';
|
|
30
|
+
|
|
31
|
+
import componentStyles from "./styles";
|
|
32
|
+
import PropTypes from "prop-types";
|
|
33
|
+
import {withTranslation} from "react-i18next";
|
|
34
|
+
import {handleHtmlEntities} from "@olea-bps/core/helper/format.helper";
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Top News Component
|
|
40
|
+
*
|
|
41
|
+
* Shows a single news item with the name and the logo of the university.
|
|
42
|
+
*
|
|
43
|
+
* Parameters:
|
|
44
|
+
* - animationRange: Animation Range for animation of the dashboard view
|
|
45
|
+
*
|
|
46
|
+
* Navigation-Parameters:
|
|
47
|
+
* - none
|
|
48
|
+
*/
|
|
49
|
+
class TopNewsComponent extends React.Component {
|
|
50
|
+
|
|
51
|
+
static propTypes = {
|
|
52
|
+
animationRange: PropTypes.any
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Styles of this component
|
|
56
|
+
styles;
|
|
57
|
+
|
|
58
|
+
animateHeader = null;
|
|
59
|
+
animateBackground = null;
|
|
60
|
+
topNews = null;
|
|
61
|
+
hasConnection = true;
|
|
62
|
+
netInfoUnsubscribe = null;
|
|
63
|
+
|
|
64
|
+
constructor(props) {
|
|
65
|
+
super(props);
|
|
66
|
+
|
|
67
|
+
// ------------------------------------------------------------------------
|
|
68
|
+
// PLUGIN FUNCTIONALITY
|
|
69
|
+
// ------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
const { pluginStyles,theme } = this.props;
|
|
72
|
+
this.styles = componentStyles(theme);
|
|
73
|
+
|
|
74
|
+
if(pluginStyles) {
|
|
75
|
+
this.styles = merge(this.styles, pluginStyles);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.styles = StyleSheet.create(this.styles);
|
|
79
|
+
// ------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
const height = Dimensions.get('window').height;
|
|
82
|
+
|
|
83
|
+
this.animateHeader = {
|
|
84
|
+
transform: [{
|
|
85
|
+
translateY: this.props.animationRange.interpolate({
|
|
86
|
+
inputRange: [0, 1],
|
|
87
|
+
outputRange: [0, height / 8],
|
|
88
|
+
})
|
|
89
|
+
}]
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
this.animateLogo = {
|
|
93
|
+
transform: [{
|
|
94
|
+
translateY: this.props.animationRange.interpolate({
|
|
95
|
+
inputRange: [0, 1],
|
|
96
|
+
outputRange: [30, (height / 50)],
|
|
97
|
+
})
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
scale: this.props.animationRange.interpolate({
|
|
101
|
+
inputRange: [0, 1],
|
|
102
|
+
outputRange: [1, .4],
|
|
103
|
+
})
|
|
104
|
+
}]
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
this.animateBackground = {
|
|
108
|
+
transform: [{
|
|
109
|
+
translateY: this.props.animationRange.interpolate({
|
|
110
|
+
inputRange: [0, 1],
|
|
111
|
+
outputRange: [0, height / 7],
|
|
112
|
+
extrapolate: 'clamp'
|
|
113
|
+
})
|
|
114
|
+
}]
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
this.netInfoUnsubscribe = NetInfo.addEventListener(state => {
|
|
118
|
+
this.hasConnection = state.isInternetReachable && state.isConnected;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
componentWillUnmount() {
|
|
123
|
+
if(this.netInfoUnsubscribe) {
|
|
124
|
+
this.netInfoUnsubscribe();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
render() {
|
|
129
|
+
|
|
130
|
+
// ------------------------------------------------------------------------
|
|
131
|
+
// PLUGIN FUNCTIONALITY
|
|
132
|
+
// ------------------------------------------------------------------------
|
|
133
|
+
const PluginComponent = this.props.pluginComponent;
|
|
134
|
+
if (PluginComponent) {
|
|
135
|
+
return <PluginComponent />;
|
|
136
|
+
}
|
|
137
|
+
// ------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
const {feeds, t} = this.props;
|
|
141
|
+
const {colors, appSettings} = this.props.theme;
|
|
142
|
+
let topNews = this.props.topNews;
|
|
143
|
+
|
|
144
|
+
if(!Array.isArray(topNews)) {
|
|
145
|
+
topNews = [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let topItem = null;
|
|
149
|
+
let imageSrc = null;
|
|
150
|
+
let feedTitle = null;
|
|
151
|
+
if(topNews[0]) {
|
|
152
|
+
topItem = topNews[0];
|
|
153
|
+
if(topItem.imageUrl) {
|
|
154
|
+
imageSrc = {uri: topItem.imageUrl};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if(feeds && topItem && topItem.originFeedId) {
|
|
159
|
+
const feed = feeds.filter(feed => feed.feedid === topItem.originFeedId)[0];
|
|
160
|
+
|
|
161
|
+
if(feed && feed.title) {
|
|
162
|
+
feedTitle = feed.title.toUpperCase();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if(!imageSrc)
|
|
167
|
+
imageSrc = appSettings.header;
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
// Top News available
|
|
171
|
+
this.topNews = topItem;
|
|
172
|
+
|
|
173
|
+
// Notice about "." in accessibilityLabels: This will set a short break in the sound output, like in a normal sentence.
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<View style={{overflow:'hidden'}}>
|
|
177
|
+
<View stlyle={this.styles.backgroundImageWrapper}>
|
|
178
|
+
<TouchableOpacity
|
|
179
|
+
onPress={
|
|
180
|
+
topItem?.link ?? null
|
|
181
|
+
? () => { this._handlePressNews(topItem) }
|
|
182
|
+
: null
|
|
183
|
+
}
|
|
184
|
+
>
|
|
185
|
+
<Animated.Image
|
|
186
|
+
source={imageSrc}
|
|
187
|
+
resizeMode="cover"
|
|
188
|
+
fadeDuration={0}
|
|
189
|
+
style={[this.styles.newsImage, this.animateBackground]}
|
|
190
|
+
accessible={true}
|
|
191
|
+
accessibilityLabel={
|
|
192
|
+
!topItem && !this.hasConnection
|
|
193
|
+
? t('home:noConnectionTitle')
|
|
194
|
+
: (!topItem ? t('home:noItemTitle') : handleHtmlEntities(topItem.title))
|
|
195
|
+
}
|
|
196
|
+
accessibilityHint={!topItem ? '' : t('accessibility:topNewsHint')}
|
|
197
|
+
/>
|
|
198
|
+
</TouchableOpacity>
|
|
199
|
+
</View>
|
|
200
|
+
|
|
201
|
+
{
|
|
202
|
+
(!topItem && !this.hasConnection) ? (
|
|
203
|
+
<Animated.View style={[this.styles.newsItem]}
|
|
204
|
+
accessible={true}
|
|
205
|
+
accessibilityLabel={t('home:noConnectionTitle')}>
|
|
206
|
+
<Text
|
|
207
|
+
style={this.styles.newsItemCategory}>{t('home:noConnectionSubtitle').toUpperCase()}</Text>
|
|
208
|
+
<Text style={this.styles.newsItemTitle}>{t('home:noConnectionTitle')}</Text>
|
|
209
|
+
<Text style={this.styles.newsItemText}>{t('home:noConnectionText')}</Text>
|
|
210
|
+
<View style={this.styles.newsItemActionbar}>
|
|
211
|
+
<Text style={this.styles.newsItemDate}></Text>
|
|
212
|
+
</View>
|
|
213
|
+
</Animated.View>
|
|
214
|
+
) : ((!topItem) ? (
|
|
215
|
+
<Animated.View style={[this.styles.newsItem]}
|
|
216
|
+
accessible={true}
|
|
217
|
+
accessibilityLabel={t('home:noItemTitle')}>
|
|
218
|
+
<Text style={this.styles.newsItemCategory}>{t('home:noItemSubtitle').toUpperCase()}</Text>
|
|
219
|
+
<Text style={this.styles.newsItemTitle}>{t('home:noItemTitle')}</Text>
|
|
220
|
+
<Text style={this.styles.newsItemText}>{t('home:noItemText')}</Text>
|
|
221
|
+
<View style={this.styles.newsItemActionbar}>
|
|
222
|
+
<Text style={this.styles.newsItemDate}/>
|
|
223
|
+
</View>
|
|
224
|
+
</Animated.View>
|
|
225
|
+
) : (topItem?.shortDesc
|
|
226
|
+
? <TouchableOpacity
|
|
227
|
+
style={this.styles.newsItem}
|
|
228
|
+
onPress={() => {this._handlePressNews(topItem)}}
|
|
229
|
+
accessible={true}
|
|
230
|
+
accessibilityLabel={handleHtmlEntities(topItem.title)}
|
|
231
|
+
accessibilityHint={t('accessibility:topNewsHint')}>
|
|
232
|
+
<View>
|
|
233
|
+
{feedTitle && <Text style={this.styles.newsItemCategory}>{feedTitle}</Text>}
|
|
234
|
+
<Text style={this.styles.newsItemTitle}>{handleHtmlEntities(topItem.title)}</Text>
|
|
235
|
+
<Text style={this.styles.newsItemText}>{handleHtmlEntities(topItem.shortDesc)} ... <Text style={this.styles.newsItemReadMore}>{t('home:readMore')}</Text></Text>
|
|
236
|
+
<View style={this.styles.newsItemActionbar}>
|
|
237
|
+
{topItem?.author != null
|
|
238
|
+
? <Text style={this.styles.newsItemAuthor}>{handleHtmlEntities(topItem.author)}</Text>
|
|
239
|
+
: null
|
|
240
|
+
}
|
|
241
|
+
</View>
|
|
242
|
+
</View>
|
|
243
|
+
</TouchableOpacity>
|
|
244
|
+
: null
|
|
245
|
+
))
|
|
246
|
+
}
|
|
247
|
+
</View>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* User has pressed the news item card.
|
|
254
|
+
* Shows the news details in a modal window.
|
|
255
|
+
*
|
|
256
|
+
* @param news
|
|
257
|
+
*
|
|
258
|
+
* @private
|
|
259
|
+
*/
|
|
260
|
+
_handlePressNews = (news) => {
|
|
261
|
+
if (news) {
|
|
262
|
+
this.props.navigation.navigate(
|
|
263
|
+
'TopNewsDetail',
|
|
264
|
+
{
|
|
265
|
+
news: {...news, 'feedId': 0},
|
|
266
|
+
newsType: "Default"
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
const mapStateToProps = state => {
|
|
274
|
+
return {
|
|
275
|
+
pluginComponent: state.pluginReducer.topNews.component,
|
|
276
|
+
pluginStyles: state.pluginReducer.topNews.styles,
|
|
277
|
+
topNews: state.apiReducer.topNews,
|
|
278
|
+
feeds: state.apiReducer.feeds
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
export default connect(mapStateToProps, null)(withTranslation()(withTheme(TopNewsComponent)))
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {Platform, Dimensions} from "react-native";
|
|
2
|
+
|
|
3
|
+
export default function(theme) {
|
|
4
|
+
const width = Dimensions.get('window').width;
|
|
5
|
+
const height = Dimensions.get('window').height;
|
|
6
|
+
const headerImageHeight = height / 2;
|
|
7
|
+
return {
|
|
8
|
+
appHeader: {
|
|
9
|
+
flex: 1,
|
|
10
|
+
flexDirection: 'row',
|
|
11
|
+
alignItems: 'baseline',
|
|
12
|
+
},
|
|
13
|
+
appHeaderSearchButton: {
|
|
14
|
+
position: 'absolute',
|
|
15
|
+
top: Platform.OS === 'ios' ? theme.paddings.default : theme.paddings.xlarge,
|
|
16
|
+
right: theme.paddings.default
|
|
17
|
+
|
|
18
|
+
},
|
|
19
|
+
appHeaderSearch: {},
|
|
20
|
+
appHeaderContainer: {
|
|
21
|
+
flex: 1,
|
|
22
|
+
justifyContent: 'center',
|
|
23
|
+
alignContent: 'center',
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
height: headerImageHeight,
|
|
26
|
+
},
|
|
27
|
+
universityTitle: {
|
|
28
|
+
marginTop: theme.paddings.large,
|
|
29
|
+
marginLeft: theme.paddings.default,
|
|
30
|
+
color: theme.colors.primaryText,
|
|
31
|
+
fontSize: theme.fontSizes.universityTitle,
|
|
32
|
+
...theme.fonts.bold,
|
|
33
|
+
lineHeight: theme.lineHeights.xxl,
|
|
34
|
+
textAlign: 'left',
|
|
35
|
+
alignSelf: 'flex-start',
|
|
36
|
+
marginBottom: theme.paddings.small
|
|
37
|
+
},
|
|
38
|
+
newsImage: {
|
|
39
|
+
aspectRatio: 16/9,
|
|
40
|
+
width: width
|
|
41
|
+
},
|
|
42
|
+
backgroundImageWrapper: {
|
|
43
|
+
height: headerImageHeight,
|
|
44
|
+
width: width,
|
|
45
|
+
marginTop: 0,
|
|
46
|
+
backgroundColor: theme.colors.primary,
|
|
47
|
+
position:'relative',
|
|
48
|
+
overflow: 'hidden'
|
|
49
|
+
},
|
|
50
|
+
backgroundImage: {
|
|
51
|
+
position:'absolute',
|
|
52
|
+
top: -10,
|
|
53
|
+
left: 0,
|
|
54
|
+
right: 0,
|
|
55
|
+
bottom: 0
|
|
56
|
+
},
|
|
57
|
+
imageOverlay: {
|
|
58
|
+
height: headerImageHeight,
|
|
59
|
+
width: width,
|
|
60
|
+
flex: 1
|
|
61
|
+
},
|
|
62
|
+
newsItem: {
|
|
63
|
+
backgroundColor: theme.colors.background,
|
|
64
|
+
paddingHorizontal: theme.paddings.default,
|
|
65
|
+
paddingTop: theme.paddings.default
|
|
66
|
+
},
|
|
67
|
+
newsItemCategory: {
|
|
68
|
+
marginBottom: 5,
|
|
69
|
+
fontSize: theme.fontSizes.s
|
|
70
|
+
},
|
|
71
|
+
newsItemTitle: {
|
|
72
|
+
...theme.fonts.bold,
|
|
73
|
+
fontSize: theme.fontSizes.xxl,
|
|
74
|
+
lineHeight: theme.lineHeights.titleBigger,
|
|
75
|
+
marginBottom: theme.paddings.small,
|
|
76
|
+
},
|
|
77
|
+
newsItemText: {
|
|
78
|
+
...theme.fonts.regular,
|
|
79
|
+
fontSize: theme.fontSizes.m,
|
|
80
|
+
lineHeight: theme.lineHeights.s
|
|
81
|
+
},
|
|
82
|
+
newsItemReadMore: {
|
|
83
|
+
textDecorationLine: 'underline'
|
|
84
|
+
},
|
|
85
|
+
newsItemActionbar: {
|
|
86
|
+
borderBottomColor: theme.colors.accent,
|
|
87
|
+
borderBottomWidth: 1,
|
|
88
|
+
//borderStyle: 'dotted', // TODO Not available on iOS at the moment (also dashed)
|
|
89
|
+
borderStyle: 'solid',
|
|
90
|
+
paddingBottom: theme.paddings.small,
|
|
91
|
+
marginTop: theme.paddings.small,
|
|
92
|
+
flex: 1,
|
|
93
|
+
flexDirection: 'row'
|
|
94
|
+
},
|
|
95
|
+
newsItemDate: {
|
|
96
|
+
fontSize: theme.fontSizes.s,
|
|
97
|
+
...theme.fonts.bold,
|
|
98
|
+
color: theme.colors.subtitle,
|
|
99
|
+
flex: 1,
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
alignSelf: 'center',
|
|
102
|
+
flexDirection: 'row',
|
|
103
|
+
},
|
|
104
|
+
newsItemAuthor: {
|
|
105
|
+
fontSize: theme.fontSizes.s,
|
|
106
|
+
...theme.fonts.bold,
|
|
107
|
+
color: theme.colors.subtitle,
|
|
108
|
+
flex: 1,
|
|
109
|
+
alignItems: 'center',
|
|
110
|
+
alignSelf: 'center',
|
|
111
|
+
flexDirection: 'row',
|
|
112
|
+
},
|
|
113
|
+
newsItemActions: {
|
|
114
|
+
flex: 1,
|
|
115
|
+
alignItems: 'flex-end',
|
|
116
|
+
justifyContent: 'flex-end'
|
|
117
|
+
},
|
|
118
|
+
newsItemAction: {
|
|
119
|
+
marginRight: -4
|
|
120
|
+
},
|
|
121
|
+
modalContainer: {
|
|
122
|
+
flex: 1
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
};
|