@olea-bps/base 1.1.5 → 1.1.7

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.
@@ -12,30 +12,35 @@
12
12
  * limitations under the License.
13
13
  */
14
14
 
15
- import React from 'react';
16
- import PropTypes from 'prop-types';
15
+ import {
16
+ useCallback,
17
+ useMemo
18
+ } from 'react';
17
19
 
18
20
  import {
19
- StyleSheet,
20
- Text,
21
- View,
21
+ Linking,
22
+ Platform,
22
23
  SafeAreaView,
24
+ ScrollView,
23
25
  Share,
24
- Linking,
26
+ StyleSheet,
27
+ Text,
25
28
  TouchableOpacity,
26
- Platform,
27
- ScrollView
29
+ View,
28
30
  } from 'react-native';
29
- import {Appbar, Headline, List, withTheme} from "react-native-paper";
31
+ import {
32
+ Headline,
33
+ List,
34
+ useTheme,
35
+ } from 'react-native-paper';
30
36
 
31
- import { connect } from 'react-redux'
32
- import {withTranslation} from "react-i18next";
33
- import merge from 'lodash/merge';
37
+ import { useTranslation } from 'react-i18next';
34
38
 
35
- import componentStyles from './styles';
36
39
  import AppbarComponent from '../AppBar';
37
40
  import AppbarAction from '../AppbarAction';
41
+ import IconListItem from '../IconListItem';
38
42
 
43
+ import componentStyles from './styles';
39
44
 
40
45
  /**
41
46
  * Contact Detail Component
@@ -43,193 +48,168 @@ import AppbarAction from '../AppbarAction';
43
48
  * Shows the detailed information of a contact with name and contact details.
44
49
  * Provides a share functionality.
45
50
  *
46
- * Parameters:
47
- * - contact: Contact object with all information about the contact
48
- *
49
- * Navigation-Parameters:
50
- * - none
51
+ * @param {object} props
52
+ * @param {object} props.contact - Der anzuzeigende Kontakt
51
53
  */
52
- class ContactDetailComponent extends React.Component {
53
- static propTypes = {
54
- contact: PropTypes.object.isRequired
55
- };
56
-
57
- // Styles of this component
58
- styles;
59
-
60
- contact = null;
61
-
62
-
63
- constructor(props) {
64
- super(props);
65
- // ------------------------------------------------------------------------
66
- // PLUGIN FUNCTIONALITY
67
- // ------------------------------------------------------------------------
68
-
69
- const { pluginStyles,theme } = this.props;
70
- this.styles = componentStyles(theme);
71
-
72
- if(pluginStyles) {
73
- this.styles = merge(this.styles, pluginStyles);
74
- }
75
-
76
- this.styles = StyleSheet.create(this.styles);
77
-
78
- // ------------------------------------------------------------------------
79
- };
80
-
81
- /**
82
- * Share function for contact
83
- *
84
- * @returns {Promise<void>}
85
- * @private
86
- */
87
- async _onShare() {
88
- try {
89
- const { t } = this.props;
90
-
91
- let phone = "";
92
- if(this.contact.telephone.length > 0) {
93
- phone += "\n"+ t('contact:phone') +": ";
94
- this.contact.telephone.forEach((phoneNumber) => {
95
- phone += phoneNumber.number + '\n';
54
+ export default function ContactDetailComponent({ contact, ...restProps }) {
55
+ const theme = useTheme();
56
+ const { themeStyles } = theme;
57
+ const { t } = useTranslation();
58
+
59
+ const styles = useMemo(
60
+ () => StyleSheet.create(componentStyles(theme)),
61
+ [theme, componentStyles]
62
+ );
63
+
64
+ const onShare = useCallback(
65
+ async () => {
66
+ try {
67
+ // Telephone-Objekte aus dem Kontakt auslesen. Falls diese nicht vorhanden sind, mit einem leeren Array initialisieren
68
+ const contactTelephones = contact?.telephone ?? [];
69
+ const shareMessageTelephoneNumbers = contactTelephones
70
+ .map(telephone => telephone.number)
71
+ .map(number => `${t('contact:phone')}: ${number}`)
72
+ .join('\n');
73
+
74
+ const shareMessage = `${t('contact:title')} - ${contact.firstName} ${contact.lastName}`
75
+ +
76
+ (
77
+ contact?.building
78
+ ? `\n${t('contact:building')}: ${contact.building}`
79
+ : ''
80
+ )
81
+ +
82
+ (
83
+ contact?.department
84
+ ? `\n${t('contact:department')}: ${contact.department}`
85
+ : ''
86
+ )
87
+ +
88
+ (
89
+ contact?.room?.title
90
+ ? `\n${t('contact:room')}: ${contact.room.title}`
91
+ : ''
92
+ )
93
+ +
94
+ (
95
+ contact?.email
96
+ ? `\n${t('contact:email')}: ${contact.email}`
97
+ : ''
98
+ )
99
+ +
100
+ (
101
+ shareMessageTelephoneNumbers
102
+ ? '\n' + shareMessageTelephoneNumbers
103
+ : ''
104
+ );
105
+
106
+ await Share.share({
107
+ message: shareMessage,
96
108
  });
109
+ } catch (error) {
110
+ alert(error.message);
97
111
  }
98
-
99
- let message = t('contact:title') + ' - ' + this.contact.firstName + ' ' + this.contact.lastName +
100
- ((this.contact.building) ? "\n" + t('contact:building') + ": " + this.contact.building : '') +
101
- ((this.contact.department) ? "\n" + t('contact:department') + ": " + this.contact.department : '') +
102
- ((this.contact.room && this.contact.room.title) ? "\n" + t('contact:room') + ": " + this.contact.room.title : '') +
103
- ((this.contact.email) ? "\n" + t('contact:email') + ": " + this.contact.email : '') +
104
- phone;
105
-
106
- const result = await Share.share({
107
- message: message
108
- });
109
-
110
- if (result.action === Share.sharedAction) {
111
- if (result.activityType) {
112
- // shared with activity type of result.activityType
113
- } else {
114
- // shared
112
+ },
113
+ [contact, t]
114
+ );
115
+
116
+ return contact
117
+ ? <SafeAreaView style={[styles.container, themeStyles.safeAreaContainer]}>
118
+ <AppbarComponent
119
+ {...restProps}
120
+ title={t('contact:contactInformation')}
121
+ rightAction={<AppbarAction icon='share' onPress={onShare} />}
122
+ />
123
+ <ScrollView style={styles.containerInner}>
124
+ <Headline style={styles.name}>{contact.firstName} {contact.lastName}</Headline>
125
+ {
126
+ contact?.building
127
+ ? <List.Item
128
+ key="building"
129
+ title={t('contact:building')}
130
+ description={contact.building}
131
+ descriptionStyle={themeStyles.textLighter}
132
+ titleStyle={themeStyles.searchDetailTitle}
133
+ descriptionNumberOfLines={200} />
134
+ : null
115
135
  }
116
- } else if (result.action === Share.dismissedAction) {
117
- // dismissed
118
- }
119
- } catch (error) {
120
- alert(error.message);
121
- }
122
- };
123
-
124
- /**
125
- * Render contact details content
126
- *
127
- * @returns {*}
128
- * @private
129
- */
130
- _renderContent = () => {
131
- const { contact, t } = this.props;
132
- const { themeStyles } = this.props.theme;
133
-
134
- let output = [];
135
- let index = 1;
136
-
137
-
138
-
139
-
140
- if(contact.building)
141
- output.push(<List.Item key="building" title={t('contact:building')} description={contact.building}
142
- descriptionStyle={themeStyles.textLighter} titleStyle={themeStyles.searchDetailTitle}
143
- descriptionNumberOfLines={200}/>);
144
-
145
- if(contact.department)
146
- output.push(<List.Item key="department" title={t('contact:department')} description={contact.department}
147
- descriptionStyle={themeStyles.textLighter} titleStyle={themeStyles.searchDetailTitle}
148
- descriptionNumberOfLines={200}/>);
149
-
150
- if(contact.room && contact.room.title)
151
- output.push(<List.Item key="room" title={t('contact:room')} description={contact.room.title}
152
- descriptionStyle={themeStyles.textLighter} titleStyle={themeStyles.searchDetailTitle}
153
- descriptionNumberOfLines={200}/>);
154
-
155
- if(contact.email)
156
- output.push(<TouchableOpacity key="email" onPress={() => Linking.openURL('mailto:' + contact.email)}>
157
- <List.Item title={t('contact:email')} description={contact.email}
158
- descriptionStyle={themeStyles.textLighter} titleStyle={themeStyles.searchDetailTitle}
159
- right={props => <List.Icon {...props} icon="email"/>}/>
160
- </TouchableOpacity>
161
- );
162
-
163
- if(contact.telephone.length > 0) {
164
- contact.telephone.forEach((phoneNumber) => {
165
- output.push(
166
- <TouchableOpacity key={'phone_'+ index} onPress={() => {
167
- const formattedPhoneNumber = phoneNumber.number.replace(/\s/g, '');
168
- Linking.openURL('tel:' + formattedPhoneNumber).catch(error => console.error(`ContactDetailComponent - Fehler beim öffnen des Telefons ${Platform.OS}`, error));
169
- }}>
170
- <List.Item
171
- title={t('contact:phone') + ' ' + index}
172
- description={phoneNumber.number}
136
+ {
137
+ contact?.department
138
+ ? <List.Item
139
+ key="department"
140
+ title={t('contact:department')}
141
+ description={contact.department}
173
142
  descriptionStyle={themeStyles.textLighter}
174
143
  titleStyle={themeStyles.searchDetailTitle}
175
- right={props =>
176
- <List.Icon {...props}
177
- icon="phone"/>}
178
- />
179
- </TouchableOpacity>
180
- );
181
- index++;
182
- });
183
- }
184
-
185
-
186
- return (
187
- <ScrollView style={this.styles.containerInner}>
188
- <Headline style={this.styles.name}>{contact.firstName} {contact.lastName}</Headline>
189
- {output}
190
- <View style={this.styles.space}/>
144
+ descriptionNumberOfLines={200} />
145
+ : null
146
+ }
147
+ {
148
+ contact?.room && contact?.room?.title
149
+ ? <List.Item
150
+ key="room"
151
+ title={t('contact:room')}
152
+ description={contact.room.title}
153
+ descriptionStyle={themeStyles.textLighter}
154
+ titleStyle={themeStyles.searchDetailTitle}
155
+ descriptionNumberOfLines={200} />
156
+ : null
157
+ }
158
+ {
159
+ contact?.email
160
+ ? <TouchableOpacity
161
+ key="email"
162
+ onPress={() => Linking.openURL('mailto:' + contact.email)}
163
+ >
164
+ <List.Item
165
+ title={t('contact:email')}
166
+ description={contact.email}
167
+ descriptionStyle={themeStyles.textLighter}
168
+ titleStyle={themeStyles.searchDetailTitle}
169
+ right={props => <List.Icon {...props} icon="email" />}
170
+ />
171
+ </TouchableOpacity>
172
+ : null
173
+ }
174
+ {
175
+ Array.isArray(contact?.telephone)
176
+ ? contact.telephone.map(
177
+ (telephone, index, telephones) => {
178
+
179
+ const title = telephones.length == 1
180
+ ? t('contact:phone')
181
+ : t('contact:phone') + ' ' + index;
182
+
183
+ const telephoneNumber = telephone.number
184
+
185
+ return (
186
+ <TouchableOpacity
187
+ key={telephoneNumber}
188
+ onPress={
189
+ () => {
190
+ const formattedPhoneNumber = telephone.number.replace(/\s/g, '');
191
+ Linking.openURL('tel:' + formattedPhoneNumber).catch(error => console.error(`ContactDetailComponent - Fehler beim öffnen des Telefons ${Platform.OS}`, error));
192
+ }
193
+ }
194
+ >
195
+ <IconListItem
196
+ title={title}
197
+ description={telephoneNumber}
198
+ descriptionStyle={themeStyles.textLighter}
199
+ titleStyle={themeStyles.searchDetailTitle}
200
+ icon={'call'}
201
+ />
202
+ </TouchableOpacity>
203
+ )
204
+ }
205
+ )
206
+ : null
207
+ }
208
+ <View style={styles.space} />
191
209
  </ScrollView>
192
- );
193
- };
194
-
195
- render() {
196
- // ------------------------------------------------------------------------
197
- // PLUGIN FUNCTIONALITY
198
- // ------------------------------------------------------------------------
199
- const PluginComponent = this.props.pluginComponent;
200
- if (PluginComponent) {
201
- return <PluginComponent />;
202
- }
203
- // ------------------------------------------------------------------------
204
-
205
- const {themeStyles} = this.props.theme;
206
- const { contact, t } = this.props;
207
-
208
- if(!contact){
209
- return (<Text>{t('contact:couldNotLoad')}</Text>);
210
- }
211
-
212
- this.contact = contact;
213
-
214
- return (
215
- <SafeAreaView style={[this.styles.container, themeStyles.safeAreaContainer]}>
216
- <AppbarComponent
217
- {...this.props}
218
- title={t('contact:contactInformation')}
219
- rightAction={<AppbarAction icon='share' onPress={this._onShare.bind(this)} />}
220
- />
221
- {this._renderContent()}
222
- </SafeAreaView>
223
- );
224
- }
225
- }
226
-
227
-
228
- const mapStateToProps = state => {
229
- return {
230
- pluginComponent: state.pluginReducer.contactDetail.component,
231
- pluginStyles: state.pluginReducer.contactDetail.styles
232
- };
233
- };
210
+ </SafeAreaView>
234
211
 
235
- export default connect(mapStateToProps, null)(withTranslation()(withTheme(ContactDetailComponent)))
212
+ : <Text>
213
+ {t('contact:couldNotLoad')}
214
+ </Text>;
215
+ }
@@ -0,0 +1,56 @@
1
+ import {
2
+ useCallback,
3
+ useMemo,
4
+ } from 'react';
5
+ import {
6
+ StyleSheet,
7
+ } from 'react-native';
8
+
9
+ import {
10
+ List,
11
+ useTheme,
12
+ } from 'react-native-paper';
13
+
14
+ import { default as Icon } from '../../libraries/icons-openasist';
15
+
16
+ import componentStyles from './styles';
17
+
18
+ /**
19
+ * Ein Listenelement, welches ein Icon auf der rechten Seite anzeigt.
20
+ * Es können alle Properties der {@link List.Item}-Komponente verwendet werden, außer die `right`-Property.
21
+ * Die `right`-Property wird für das Einbinden des Icons benutzt.
22
+ *
23
+ * @param {object} props
24
+ * @param {string} props.icon Iconname des anzuzeigenen Icons. Für mögliche Werte siehe {@link Icon}
25
+ */
26
+ export default function IconListItem({ icon, ...listItemProps }) {
27
+ const theme = useTheme();
28
+
29
+ const styles = useMemo(
30
+ () => StyleSheet.create(componentStyles(theme)),
31
+ [theme, componentStyles]
32
+ );
33
+
34
+ const listItemIcon = useCallback(
35
+ listIconProps =>
36
+ <List.Icon
37
+ {...listIconProps}
38
+ icon={
39
+ props =>
40
+ <Icon
41
+ {...props}
42
+ icon={icon}
43
+ />
44
+ }
45
+ />
46
+ ,
47
+ [icon]
48
+ );
49
+
50
+ return (
51
+ <List.Item
52
+ {...listItemProps}
53
+ right={listItemIcon}
54
+ />
55
+ );
56
+ }
@@ -0,0 +1,4 @@
1
+ export default function (theme) {
2
+ return {
3
+ }
4
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olea-bps/base",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "OLEA base library",
5
5
  "main": "index.js",
6
6
  "repository": {