@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.
Files changed (79) hide show
  1. package/AppBar/index.js +83 -0
  2. package/AppBar/styles.js +27 -0
  3. package/BookDetail/index.js +268 -0
  4. package/BookDetail/styles.js +124 -0
  5. package/Component/342/200/216JobsFilter/index.js +188 -0
  6. package/Component/342/200/216JobsFilter/styles.js +12 -0
  7. package/ConnectivityWarning/index.js +65 -0
  8. package/ConnectivityWarning/styles.js +19 -0
  9. package/ContactDetail/index.js +232 -0
  10. package/ContactDetail/styles.js +32 -0
  11. package/CourseDetail/index.js +357 -0
  12. package/CourseDetail/styles.js +59 -0
  13. package/CourseDetailDialog/index.js +169 -0
  14. package/CourseDetailDialog/styles.js +116 -0
  15. package/CourseInfo/index.js +219 -0
  16. package/CourseInfo/styles.js +40 -0
  17. package/DevelopmentDialog/index.js +208 -0
  18. package/DevelopmentDialog/styles.js +10 -0
  19. package/EventCodeInput/index.js +146 -0
  20. package/EventCodeInput/styles.js +108 -0
  21. package/FlexMenuEntry/index.js +84 -0
  22. package/FlexMenuEntry/styles.js +27 -0
  23. package/MainMenuEntry/index.js +88 -0
  24. package/MainMenuEntry/styles.js +28 -0
  25. package/MealItem/index.js +87 -0
  26. package/MealItem/styles.js +73 -0
  27. package/MensaMenu/index.js +307 -0
  28. package/MensaMenu/styles.js +94 -0
  29. package/MensaSlider/index.js +184 -0
  30. package/MensaSlider/styles.js +53 -0
  31. package/Modal/index.js +106 -0
  32. package/Modal/styles.js +8 -0
  33. package/NewsDetail/index.js +377 -0
  34. package/NewsDetail/styles.js +77 -0
  35. package/NewsList/index.js +120 -0
  36. package/NewsList/styles.js +19 -0
  37. package/NewsListItem/index.js +89 -0
  38. package/NewsListItem/styles.js +32 -0
  39. package/OtherCourses/index.js +152 -0
  40. package/OtherCourses/styles.js +10 -0
  41. package/PtsDeparture/index.js +140 -0
  42. package/PtsDeparture/styles.js +7 -0
  43. package/PtsStation/index.js +183 -0
  44. package/PtsStation/styles.js +47 -0
  45. package/QuickLinks/index.js +127 -0
  46. package/QuickLinks/styles.js +45 -0
  47. package/RoomDetail/index.js +281 -0
  48. package/RoomDetail/styles.js +56 -0
  49. package/ScaledImage/index.js +92 -0
  50. package/SearchResults/index.js +362 -0
  51. package/SearchResults/styles.js +59 -0
  52. package/SettingSection/index.js +54 -0
  53. package/SettingSection/styles.js +15 -0
  54. package/SettingsDialog/index.js +52 -0
  55. package/SettingsDialog/styles.js +12 -0
  56. package/SettingsDialogRadio/index.js +66 -0
  57. package/SettingsDialogRadio/styles.js +7 -0
  58. package/SettingsDialogSelect/index.js +73 -0
  59. package/SettingsDialogSelect/styles.js +7 -0
  60. package/TimetableCodeInput/index.js +201 -0
  61. package/TimetableCodeInput/styles.js +28 -0
  62. package/TimetableDay/index.js +266 -0
  63. package/TimetableDay/styles.js +103 -0
  64. package/TimetableEvent/index.js +163 -0
  65. package/TimetableEvent/styles.js +108 -0
  66. package/TimetableList/index.js +116 -0
  67. package/TimetableList/styles.js +109 -0
  68. package/TimetableMonth/index.js +156 -0
  69. package/TimetableMonth/styles.js +29 -0
  70. package/TimetableWeek/index.js +245 -0
  71. package/TimetableWeek/styles.js +58 -0
  72. package/TopNews/index.js +282 -0
  73. package/TopNews/styles.js +125 -0
  74. package/TopNewsHtwk/index.js +279 -0
  75. package/TopNewsHtwk/styles.js +142 -0
  76. package/WebView/index.js +108 -0
  77. package/WebView/styles.js +11 -0
  78. package/index.js +39 -0
  79. package/package.json +37 -0
@@ -0,0 +1,127 @@
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 { StyleSheet, Text, View, Linking, TouchableOpacity, useWindowDimensions } from 'react-native';
17
+ import { withTheme, Badge } from "react-native-paper";
18
+ import { useTranslation } from 'react-i18next';
19
+
20
+ import componentStyles from "./styles"
21
+ import IconsOpenasist from "@olea-bps/icons-openasist";
22
+
23
+ /**
24
+ * Quicklink Component
25
+ *
26
+ * Shows important links to university webside.
27
+ *
28
+ * Parameters:
29
+ * - none
30
+ *
31
+ * Navigation-Parameters:
32
+ * - none
33
+ */
34
+ function QuickLinksComponent({ theme, theme: { colors } }) {
35
+ const { t, i18n } = useTranslation();
36
+ const { width, height } = useWindowDimensions();
37
+
38
+ const quicklinks = theme?.appSettings?.modules?.dashboard?.quicklinks ?? [];
39
+
40
+ const styles = useMemo(
41
+ () => StyleSheet.create(componentStyles(theme, width, height)),
42
+ [theme, width, height]
43
+ )
44
+
45
+ return (
46
+ // If quicklinks are in settings.js defined return content, otherwise return nothing to render
47
+ Array.isArray(quicklinks) && quicklinks.length > 0
48
+ ?
49
+ <View style={styles.container}>
50
+ <View style={[styles.innerContainer, { alignItems: "center" }]}>
51
+ <Text style={[styles.headline, { marginRight: 5 }]}>{t('quicklinks:title')}</Text>
52
+ <IconsOpenasist
53
+ icon={"open-external"}
54
+ color={colors.secondaryText}
55
+ size={10}
56
+ accessibilityLabel={t('accessibility:quicklinks.externalLinkSymbol')}
57
+ />
58
+ </View>
59
+
60
+ <View style={
61
+ [
62
+ styles.innerContainer,
63
+ {
64
+ justifyContent: quicklinks.length > 2
65
+ ? 'space-between'
66
+ : 'space-around'
67
+ },
68
+ ]}
69
+ >
70
+ {
71
+ quicklinks.map(
72
+ quicklink => {
73
+ const title = t(quicklink?.title);
74
+ const accessibilityTitle = i18n.exists(quicklink?.accessibilityTitle) ? t(quicklink?.accessibilityTitle) : title;
75
+ const icon = quicklink?.icon;
76
+ const url = i18n.exists(quicklink?.url) ? t(quicklink?.url) : quicklink?.url;
77
+ const badgeContent = quicklink?.badgeContent?.();
78
+
79
+ return (
80
+ <TouchableOpacity
81
+ key={`${title}_${icon}`}
82
+ accessibilityLabel={accessibilityTitle}
83
+ accessibilityRole='button'
84
+ onPress={
85
+ () =>
86
+ Linking.canOpenURL(url)
87
+ .then(
88
+ supported => {
89
+ if (supported) {
90
+ Linking.openURL(url);
91
+ }
92
+ }
93
+ )
94
+ }
95
+ >
96
+ <View style={styles.roundButtons}>
97
+ <IconsOpenasist icon={icon} color={colors.primaryText} size={35} />
98
+ <View style={styles.linkextern}>
99
+ <Text style={styles.quicklinkLabel}>
100
+ {title}
101
+ </Text>
102
+ </View>
103
+ </View >
104
+ <View style={styles.badgeContainer}>
105
+ <Badge
106
+ visible={
107
+ badgeContent
108
+ ? true
109
+ : false
110
+ }
111
+ style={styles.badge}
112
+ >
113
+ {badgeContent}
114
+ </Badge>
115
+ </View>
116
+ </TouchableOpacity>
117
+ )
118
+ }
119
+ )
120
+ }
121
+ </View>
122
+ </View >
123
+ : null
124
+ );
125
+ }
126
+
127
+ export default (withTheme(QuickLinksComponent))
@@ -0,0 +1,45 @@
1
+ export default function (theme, width, height) {
2
+
3
+ const roundButtonWidth = width / 5;
4
+
5
+ return {
6
+ container: {
7
+ flex: 1,
8
+ flexDirection: 'column',
9
+ paddingHorizontal: theme.paddings.default,
10
+ paddingVertical: theme.paddings.small,
11
+ paddingTop: theme.paddings.xsmall,
12
+ },
13
+ innerContainer: {
14
+ flex: 1,
15
+ flexDirection: 'row',
16
+ paddingVertical: theme.paddings.small,
17
+ },
18
+ headline: {
19
+ ...theme.fonts.regular,
20
+ fontSize: theme.fontSizes.m,
21
+ textTransform: 'uppercase'
22
+ },
23
+ roundButtons: {
24
+ width: roundButtonWidth,
25
+ height: roundButtonWidth,
26
+ borderRadius: roundButtonWidth / 2,
27
+ backgroundColor: theme.colors.quicklinksBackground,
28
+ justifyContent: "center",
29
+ alignItems: "center"
30
+ },
31
+ badgeContainer: {
32
+ position: 'absolute',
33
+ width: roundButtonWidth,
34
+ },
35
+ badge: {
36
+ backgroundColor: 'red',
37
+ },
38
+ quicklinkLabel: {
39
+ color: theme.colors.primaryText,
40
+ fontSize: theme.fontSizes.s,
41
+ lineHeight: theme.fontSizes.subtitle * 1.1,
42
+ textAlign: 'center',
43
+ },
44
+ }
45
+ };
@@ -0,0 +1,281 @@
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
+ SafeAreaView,
18
+ StyleSheet,
19
+ View,
20
+ Share,
21
+ TouchableOpacity, ActivityIndicator
22
+ } from 'react-native';
23
+ import {connect} from 'react-redux'
24
+ import {Appbar, Headline, List, withTheme} from "react-native-paper";
25
+ import {withTranslation} from "react-i18next";
26
+ import merge from 'lodash/merge';
27
+
28
+
29
+ import { AppBar as AppbarComponent } from '@olea-bps/components';
30
+ import IconsOpenasist from "@olea-bps/icons-openasist";
31
+
32
+ import componentStyles from "./styles";
33
+ import {WebView} from "react-native-webview";
34
+
35
+ /**
36
+ * Room Detail Component
37
+ *
38
+ * Shows the webview for the room directory.
39
+ *
40
+ * Parameters:
41
+ * - none
42
+ *
43
+ * Navigation-Parameters:
44
+ * - none
45
+ */
46
+ class RoomDetailComponent extends React.Component {
47
+ static navigationOptions = {
48
+ header: null
49
+ };
50
+
51
+
52
+ state = {
53
+ refreshing: false,
54
+ webview: {
55
+ url: '',
56
+ title: ''
57
+ }
58
+ };
59
+
60
+ // Styles of this component
61
+ styles;
62
+
63
+ webView = {
64
+ canGoBack: false,
65
+ ref: null,
66
+ };
67
+
68
+ constructor(props) {
69
+ super(props);
70
+
71
+ // ------------------------------------------------------------------------
72
+ // PLUGIN FUNCTIONALITY
73
+ // ------------------------------------------------------------------------
74
+
75
+ const {pluginStyles, theme} = this.props;
76
+ this.styles = componentStyles(theme);
77
+
78
+ if (pluginStyles) {
79
+ this.styles = merge(this.styles, pluginStyles);
80
+ }
81
+
82
+ this.styles = StyleSheet.create(this.styles);
83
+
84
+ // ------------------------------------------------------------------------
85
+ }
86
+
87
+ /**
88
+ * Share function for room
89
+ *
90
+ * @returns {Promise<void>}
91
+ * @private
92
+ */
93
+ async _onShare() {
94
+ try {
95
+ const { room, theme, t } = this.props;
96
+ let roomTitle = room.code2017 || room.title;
97
+ roomTitle += room.codeDez1 ? ' (' + t('room:old') + ': ' + room.codeDez1 + ')' : '' ;
98
+
99
+ let message = t('room:title') + ' - ' + roomTitle +
100
+ ((room.building.name) ? "\n" + t('room:building') + ": " + room.building.name : '') +
101
+ ((room.building.postaladdress) ? "\n" + t('room:address') + ": " + room.building.postaladdress + ", " + (room.building.postalcode ? "\n" + room.building.postalcode + ' ' + theme.appSettings.defaultCity : '') : '') +
102
+ ((room.codeDez1) ? "\n" + t('room:campusFinderLink') + ": " + room.url : '') +
103
+ ((room.codeDez1) ? "\n" + t('room:googleMapsLink') + ": " + theme.appSettings.googleMapsUrl + room.building.coordinates.replace(/\s/g,'') : '');
104
+
105
+ const result = await Share.share({
106
+ message: message
107
+ });
108
+
109
+ if (result.action === Share.sharedAction) {
110
+ if (result.activityType) {
111
+ // shared with activity type of result.activityType
112
+ } else {
113
+ // shared
114
+ }
115
+ } else if (result.action === Share.dismissedAction) {
116
+ // dismissed
117
+ }
118
+ } catch (error) {
119
+ alert(error.message);
120
+ }
121
+ };
122
+
123
+
124
+ /**
125
+ * Render room details content
126
+ *
127
+ * @returns a view that includes the full name of the room, its building plus address, one button for opening the campus-navigator and one for opening google-maps
128
+ * @private
129
+ */
130
+ _renderContent = () => {
131
+ const { room, t, theme } = this.props;
132
+ const { themeStyles } = theme;
133
+ const { colors } = theme;
134
+
135
+ let roomTitle = room.code2017 || room.title;
136
+ roomTitle += room.codeDez1 ? ' (' + t('room:old') + ': ' + room.codeDez1 + ')' : '';
137
+
138
+ return (
139
+ <View style={this.styles.containerInner}>
140
+ <Headline style={this.styles.titleHeadline}>{roomTitle}</Headline>
141
+ {
142
+ room?.building ?? null
143
+ ? <>
144
+ <List.Item
145
+ key="building"
146
+ title={t('room:building')}
147
+ description={room.building.name}
148
+ descriptionStyle={themeStyles.textLighter}
149
+ titleStyle={themeStyles.searchDetailTitle}
150
+ />
151
+ <List.Item
152
+ key="address"
153
+ title={t('room:address')}
154
+ description={
155
+ room.building.postaladdress + (
156
+ room?.building?.postalcode ?? false
157
+ ? "\n" + room.building.postalcode + ' ' + theme.appSettings.defaultCity
158
+ : ''
159
+ )
160
+ }
161
+ descriptionStyle={themeStyles.textLighter} titleStyle={themeStyles.searchDetailTitle}
162
+ />
163
+ </>
164
+ : null
165
+ }
166
+ <View style={this.styles.buttonContainer} key={'buttonContainer'}>
167
+ {
168
+ room?.url ?? null
169
+ ? <TouchableOpacity
170
+ style={{ ...this.styles.button, ...this.styles.campusFinderUrl }}
171
+ key={'campusFinderUrl'}
172
+ onPress={() => {
173
+ this.setState({
174
+ webview: {
175
+ url: room.url,
176
+ title: t('room:campusFinder')
177
+ }
178
+ })
179
+ }}
180
+ >
181
+ <IconsOpenasist
182
+ icon={'map-search'}
183
+ size={25}
184
+ color={colors.messages.noticeText}
185
+ />
186
+ <Headline
187
+ style={this.styles.buttonText}
188
+ >
189
+ {t('room:campusFinderLink')}
190
+ </Headline>
191
+ </TouchableOpacity>
192
+ : null
193
+ }
194
+ {
195
+ room?.building?.coordinates ?? null
196
+ ? <TouchableOpacity
197
+ style={this.styles.button}
198
+ key={'googleMapsUrl'}
199
+ onPress={() => {
200
+ this.setState({
201
+ webview: {
202
+ url: theme.appSettings.googleMapsUrl + room.building.coordinates,
203
+ title: t('room:googleMaps')
204
+ }
205
+ })
206
+ }}
207
+ >
208
+ <IconsOpenasist
209
+ icon={'location'}
210
+ size={25}
211
+ color={colors.messages.noticeText}
212
+ />
213
+ <Headline
214
+ style={this.styles.buttonText}
215
+ >
216
+ {t('room:googleMapsLink')}
217
+ </Headline>
218
+ </TouchableOpacity>
219
+ : null
220
+ }
221
+ </View>
222
+ </View>
223
+ );
224
+ };
225
+
226
+
227
+ render() {
228
+ // ------------------------------------------------------------------------
229
+ // PLUGIN FUNCTIONALITY
230
+ // ------------------------------------------------------------------------
231
+ const PluginComponent = this.props.pluginComponent;
232
+ if (PluginComponent) {
233
+ return <PluginComponent />;
234
+ }
235
+ // ------------------------------------------------------------------------
236
+
237
+ const {themeStyles, customScript} = this.props.theme;
238
+ const {t} = this.props;
239
+ const {title, url} = this.state.webview;
240
+ const {colors} = this.props.theme;
241
+
242
+ if(title && url) {
243
+ return (
244
+ <SafeAreaView style={[this.styles.container, themeStyles.safeAreaContainer]}>
245
+ <AppbarComponent {...this.props}
246
+ title={title}
247
+ leftAction={<Appbar.Action
248
+ icon={props => <IconsOpenasist {...props} icon={'back'} color={colors.primaryText} /> }
249
+ onPress={() => this.setState({webview: {title: '', url: ''}})} />}/>
250
+ <WebView
251
+ ref={ref => this.webView.ref = ref}
252
+ source={{uri: url}}
253
+ injectedJavaScript={customScript}
254
+ startInLoadingState={true}
255
+ renderLoading={() => <ActivityIndicator style={this.styles.activity} size="large" color={colors.loadingIndicator}/>}
256
+ onNavigationStateChange={(navState) => { this.webView.canGoBack = navState.canGoBack; }}
257
+ />
258
+ </SafeAreaView>
259
+ );
260
+ }
261
+
262
+
263
+ return (
264
+ <SafeAreaView style={[this.styles.container, themeStyles.safeAreaContainer]}>
265
+ <AppbarComponent {...this.props}
266
+ title={t('room:directory')}
267
+ rightAction={<Appbar.Action icon="share-variant" onPress={this._onShare.bind(this)}/>}/>
268
+ {this._renderContent()}
269
+ </SafeAreaView>
270
+ );
271
+ }
272
+ }
273
+
274
+ const mapStateToProps = state => {
275
+ return {
276
+ pluginComponent: state.pluginReducer.roomDirectory.component,
277
+ pluginStyles: state.pluginReducer.roomDirectory.styles,
278
+ };
279
+ };
280
+
281
+ export default connect(mapStateToProps, null)(withTranslation()(withTheme(RoomDetailComponent)))
@@ -0,0 +1,56 @@
1
+ export default function(theme) {
2
+ return {
3
+ titleHeadline : {
4
+ ...theme.fonts.medium,
5
+ fontSize: theme.fontSizes.xl,
6
+ lineHeight: theme.lineHeights.l,
7
+ marginBottom: theme.paddings.small
8
+ },
9
+ container: {
10
+ flex: 1,
11
+ },
12
+ containerInner: {
13
+ flex: 1,
14
+ paddingHorizontal: 20,
15
+ paddingVertical: 20
16
+ },
17
+ activity: {
18
+ marginTop: 20
19
+ },
20
+ badges: {
21
+ flexDirection: 'row',
22
+ flexWrap: 'wrap',
23
+ paddingHorizontal: 10
24
+ },
25
+ badge: {
26
+ marginRight: 5,
27
+ marginBottom: 5
28
+ },
29
+ title: {
30
+ paddingLeft: 15
31
+ },
32
+ buttonText: {
33
+ color: theme.colors.messages.noticeText,
34
+ ...theme.fonts.medium,
35
+ fontSize: theme.fontSizes.xl,
36
+ lineHeight: theme.lineHeights.l,
37
+ marginLeft: theme.paddings.small
38
+ },
39
+ buttonContainer: {
40
+ flex: 1,
41
+ justifyContent: 'flex-end',
42
+ marginBottom: theme.paddings.large + theme.paddings.default
43
+ },
44
+ button: {
45
+ ...theme.themeStyles.button,
46
+ flexDirection: 'row',
47
+ flexWrap: 'wrap',
48
+ justifyContent: 'center',
49
+ alignItems: 'center',
50
+ padding: theme.paddings.small,
51
+ },
52
+ campusFinderUrl: {
53
+ marginBottom: theme.paddings.small + theme.paddings.xsmall
54
+ }
55
+ };
56
+ };
@@ -0,0 +1,92 @@
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 {Image, View} from 'react-native';
17
+ import PropTypes from "prop-types";
18
+
19
+ /**
20
+ * Scaled Image Component
21
+ *
22
+ * This component calculates the height and width of the image (provided via uri)
23
+ * and renders the image in correct height an width. If a width and / or height is set,
24
+ * the image will be resized according to the provided values.
25
+ *
26
+ * Parameters:
27
+ * - uri: Image Uri
28
+ * - width: Desired width of the image (height will be calculated)
29
+ * - height: Desired height of the image (width will be calculated)
30
+ *
31
+ * Navigation-Parameters:
32
+ * - none
33
+ *
34
+ *
35
+ *
36
+ * Sample usage (will render a image with full width of the current device):
37
+ *
38
+ <ScaledImageComponent uri={news.imageUrl} width={Dimensions.get('window').width} />
39
+ */
40
+ export default class ScaledImageComponent extends React.Component {
41
+
42
+ constructor(props) {
43
+ super(props);
44
+ this.state = {
45
+ source: { uri: this.props.uri },
46
+ width: this.props.width || undefined,
47
+ height: this.props.height || undefined
48
+ };
49
+ }
50
+
51
+ componentDidMount() {
52
+ Image.getSize(this.props.uri, (width, height) => {
53
+ if (this.props.width && !this.props.height) {
54
+ this.setState({
55
+ width: this.props.width,
56
+ height: height * (this.props.width / width)
57
+ });
58
+ } else if (!this.props.width && this.props.height) {
59
+ this.setState({
60
+ width: width * (this.props.height / height),
61
+ height: this.props.height
62
+ });
63
+ } else {
64
+ this.setState({ width: width, height: height });
65
+ }
66
+ });
67
+ }
68
+
69
+ render() {
70
+ // Don't render until dimensions are calculated
71
+ if (!this.state.width || !this.state.height) {
72
+ return <View style={this.props.style && this.props.style} />;
73
+ }
74
+
75
+ return (
76
+ <View style={this.props.style && this.props.style}>
77
+ <Image
78
+ source={this.state.source}
79
+ style={{ height: this.state.height, width: this.state.width }}
80
+ />
81
+ </View>
82
+ );
83
+ }
84
+ }
85
+
86
+
87
+
88
+ ScaledImageComponent.propTypes = {
89
+ uri: PropTypes.string.isRequired,
90
+ width: PropTypes.number,
91
+ height: PropTypes.number
92
+ };