@olea-bps/base 1.1.11 → 1.1.12

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.
@@ -14,7 +14,14 @@
14
14
 
15
15
  import { createContext, useState, useEffect, useContext, useMemo, useReducer, useCallback } from 'react'
16
16
 
17
- import { useAutoDiscovery, makeRedirectUri, useAuthRequest, exchangeCodeAsync, refreshAsync } from 'expo-auth-session';
17
+ import {
18
+ useAutoDiscovery,
19
+ makeRedirectUri,
20
+ useAuthRequest,
21
+ exchangeCodeAsync,
22
+ refreshAsync,
23
+ fetchUserInfoAsync,
24
+ } from 'expo-auth-session';
18
25
  import * as WebBrowser from 'expo-web-browser';
19
26
  import * as SecureStore from 'expo-secure-store';
20
27
 
@@ -59,9 +66,16 @@ import { DateTime } from 'luxon';
59
66
  */
60
67
 
61
68
  class LoginError extends Error {
62
- constructor(message) {
69
+
70
+ type;
71
+ error;
72
+
73
+ constructor(message, type, error) {
63
74
  super(message);
64
75
  this.name = this.constructor.name;
76
+
77
+ this.type = type;
78
+ this.error = error;
65
79
  }
66
80
  }
67
81
 
@@ -225,72 +239,38 @@ function UserContextProvider({ children }) {
225
239
  useEffect(
226
240
  () => {
227
241
  if (accessToken && discovery?.userInfoEndpoint) {
228
- fetch(
229
- discovery.userInfoEndpoint,
230
- {
231
- headers: {
232
- 'Authorization': 'Bearer ' + accessToken,
242
+ fetchUserInfoAsync({ accessToken }, discovery)
243
+ .then(
244
+ userInfo => {
245
+ console.debug(componentName, ':', 'fetch user', ':', userInfo);
246
+
247
+ const userInfos = {
248
+ ...userInfo,
249
+ organizational_units: userInfo?.ou?.map(
250
+ (organizational_unit_name, index) => ({
251
+ name: organizational_unit_name,
252
+ number: userInfo?.ounumber?.[index],
253
+ short_name: userInfo?.oushort?.[index],
254
+ })
255
+ ) ?? [],
256
+ member_types: [
257
+ ...(userInfo?.employeetype?.includes?.('Student') ? ['student'] : []),
258
+ ...(userInfo?.employeetype?.includes?.('Mitarbeiter') ? ['employee'] : []),
259
+ ]
260
+ };
261
+
262
+ console.debug(componentName, ':', 'convert response to user', ':', userInfos);
263
+
264
+ setUser(userInfos);
233
265
  }
234
- }
235
- ).then(
236
- response => response.json()
237
- ).then(
238
- userInfoJsonResponse => {
239
- console.debug(componentName, ':', 'fetch user', ':', userInfoJsonResponse);
240
-
241
- const userInfos = {
242
- ...userInfoJsonResponse,
243
- organizational_units: userInfoJsonResponse?.ou?.map(
244
- (organizational_unit_name, index) => ({
245
- name: organizational_unit_name,
246
- number: userInfoJsonResponse?.ounumber?.[index],
247
- short_name: userInfoJsonResponse?.oushort?.[index],
248
- })
249
- ) ?? [],
250
- member_types: [
251
- ...(userInfoJsonResponse?.employeetype?.includes?.('Student') ? ['student'] : null),
252
- ...(userInfoJsonResponse?.employeetype?.includes?.('Mitarbeiter') ? ['employee'] : null),
253
- ]
254
- };
255
-
256
- console.debug(componentName, ':', 'convert response to user', ':', userInfos);
257
-
258
- setUser(userInfos);
259
- }
260
- ).catch(
261
- reason => console.warn(componentName, ':', 'can`t fetch user infos', ':', reason)
262
- );
266
+ ).catch(
267
+ reason => console.warn(componentName, ':', 'can`t fetch user infos', ':', reason)
268
+ );
263
269
  }
264
270
  },
265
271
  [accessToken, discovery]
266
272
  );
267
273
 
268
- // Wenn das Format vom Server stimmt wird kann der oberer Hook mit diesem ersetzt werden
269
- //useEffect(
270
- // () => {
271
- // if (accessToken && discovery) {
272
- // fetchUserInfoAsync(
273
- // {
274
- // accessToken: accessToken
275
- // },
276
- // discovery,
277
- // ).then(
278
- // userInfo => console.warn(userInfo)
279
- // ).then(
280
- // response => response.json()
281
- // ).then(
282
- // jsonResponse => {
283
- // console.debug(componentName, ':', 'fetch user ', jsonResponse);
284
- // setUser(jsonResponse);
285
- // }
286
- // ).catch(
287
- // reason => console.warn(componentName, ':', 'can`t fetch user infos', ':', reason)
288
- // );
289
- // }
290
- // },
291
- // [accessToken, discovery]
292
- //);
293
-
294
274
  useEffect(
295
275
  () => {
296
276
  if (refreshToken && refreshTokenAt && discovery) {
@@ -300,7 +280,7 @@ function UserContextProvider({ children }) {
300
280
  () =>
301
281
  refreshAsync(
302
282
  {
303
- clientId, clientId,
283
+ clientId: clientId,
304
284
  refreshToken: refreshToken,
305
285
  },
306
286
  discovery,
@@ -339,8 +319,13 @@ function UserContextProvider({ children }) {
339
319
  reason => console.log(componentName, ': tokenResponse: Fehler', ':', reason)
340
320
  );
341
321
  } else {
322
+ console.debug(componentName, ':', 'login error', ':', codeResponse.type, '(', JSON.stringify(codeResponse.error), ')')
342
323
  // TODO: Prüfen warum der Login nicht klappte und für die einzeilnen Gründe, genauere Fehlerinfos zurückgeben.
343
- throw new LoginError(`Login Failed: ${codeResponse?.type}`);
324
+ throw new LoginError(
325
+ `Login Failed: ${codeResponse?.type}`,
326
+ codeResponse.type,
327
+ codeResponse.error,
328
+ );
344
329
  }
345
330
  }
346
331
  );
package/index.js CHANGED
@@ -32,6 +32,7 @@ export { default as SettingsDialog } from './components/SettingsDialog';
32
32
  export { default as SettingsDialogRadio } from './components/SettingsDialogRadio';
33
33
  export { default as SettingsDialogSelect } from './components/SettingsDialogSelect';
34
34
  export { default as SettingSection } from './components/SettingSection';
35
+ export { default as TabBarIcon } from './components/TabBarIcon';
35
36
  export { default as TimetableCodeInput } from './components/TimetableCodeInput';
36
37
  export { default as TimetableDay } from './components/TimetableDay';
37
38
  export { default as TimetableEvent } from './components/TimetableEvent';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olea-bps/base",
3
- "version": "1.1.11",
3
+ "version": "1.1.12",
4
4
  "description": "OLEA base library",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -174,7 +174,7 @@ export default function PublicTransportTicketView() {
174
174
  </ScrollView>
175
175
  : <>
176
176
  <Text>
177
- Sie sind nicht eingeloogt
177
+ {t('user:notLoggedIn')}
178
178
  </Text>
179
179
  <Button
180
180
  onPress={
@@ -183,10 +183,38 @@ export default function PublicTransportTicketView() {
183
183
  ? login()
184
184
  .catch(
185
185
  reason => {
186
- console.debug(componentName, ':', 'login failed', ':', reason);
186
+ console.debug(componentName, ':', 'login failed', ':', reason, '(', JSON.stringify({ type: reason.type, error: reason.error }, ')'))
187
+
188
+ let loginFailedInfoMessage = t('user:tryLoginAgain');
189
+ switch (reason.type) {
190
+ case 'dismiss': {
191
+ loginFailedInfoMessage = [
192
+ t('user:tryLoginAgain'),
193
+ ].join('\n');
194
+ break;
195
+ }
196
+ case 'error': {
197
+ const error_code = reason.error.code
198
+
199
+ loginFailedInfoMessage = [
200
+ t('user:tryLoginAgain'),
201
+ t(`user:error:${error_code}`),
202
+ JSON.stringify({ error: reason.error }),
203
+ ].join('\n');
204
+ break;
205
+ }
206
+ default: {
207
+ loginFailedInfoMessage = [
208
+ t('user:tryLoginAgain'),
209
+ `Errortype: ${reason.type}`,
210
+ JSON.stringify(reason.error),
211
+ ].join('\n');
212
+ }
213
+ }
214
+
187
215
  Alert.alert(
188
216
  t('user:loginFailed'),
189
- t('user:tryLoginAgain'),
217
+ loginFailedInfoMessage,
190
218
  );
191
219
  }
192
220
  )
@@ -12,16 +12,16 @@
12
12
  * limitations under the License.
13
13
  */
14
14
 
15
- import React from 'react';
16
- import {SafeAreaView, View, Image, TouchableOpacity, Linking} from 'react-native';
15
+ import React from 'react'
16
+ import { SafeAreaView, View, ScrollView, Image, TouchableOpacity, Linking } from 'react-native'
17
17
 
18
- import {List, withTheme} from 'react-native-paper';
19
- import {withTranslation} from "react-i18next";
18
+ import { List, Text, withTheme } from 'react-native-paper'
19
+ import { withTranslation } from 'react-i18next'
20
20
 
21
- import * as Application from 'expo-application';
21
+ import * as Application from 'expo-application'
22
22
 
23
- import AppbarComponent from '../../components/AppBar';
24
- import componentStyles from "./styles";
23
+ import AppbarComponent from '../../components/AppBar'
24
+ import componentStyles from './styles'
25
25
 
26
26
  /**
27
27
  * Settings app info
@@ -33,54 +33,73 @@ import componentStyles from "./styles";
33
33
  * - none
34
34
  */
35
35
  class AppInfoSettingsView extends React.Component {
36
- // Styles of this component
37
- styles;
36
+ // Styles of this component
37
+ styles
38
38
 
39
- constructor(props) {
40
- super(props);
39
+ constructor (props) {
40
+ super(props)
41
41
 
42
- const {theme} = this.props;
43
- this.styles = componentStyles(theme);
44
- }
42
+ const { theme } = this.props
43
+ this.styles = componentStyles(theme)
44
+ }
45
45
 
46
- render() {
47
- const { theme: { themeStyles, appSettings }, t } = this.props;
46
+ render () {
47
+ const { theme: { themeStyles, appSettings }, t } = this.props
48
+ const { legalNotices } = appSettings
48
49
 
49
- return (
50
- <SafeAreaView style={[this.styles.container, themeStyles.appSafeAreaContainer]}>
51
- <AppbarComponent
52
- {...this.props}
53
- title={t('settings:version.title')}
54
- />
55
- <View style={themeStyles.container}>
56
- <List.Section>
57
- <List.Item
58
- key="version"
59
- title={t('settings:version.version')}
60
- description={Application.nativeApplicationVersion ?? t("settings:version.unknown")}
61
- titleNumberOfLines={2}
62
- titleStyle={this.styles.listItemTitle}
63
- descriptionStyle={this.styles.listItemDescription}
64
- >
65
- </List.Item>
66
- </List.Section>
50
+ return (
51
+ <SafeAreaView style={[this.styles.container, themeStyles.appSafeAreaContainer]}>
52
+ <AppbarComponent
53
+ {...this.props}
54
+ title={t('settings:version.title')}
55
+ />
56
+ <View style={themeStyles.container}>
57
+ <ScrollView>
58
+ <List.Section>
59
+ <List.Item
60
+ key='version'
61
+ title={t('settings:version.version')}
62
+ description={Application.nativeApplicationVersion ?? t('settings:version.unknown')}
63
+ titleNumberOfLines={2}
64
+ titleStyle={this.styles.listItemTitle}
65
+ descriptionStyle={this.styles.listItemDescription}
66
+ />
67
+ {legalNotices?.licenseText && (
68
+ <List.Accordion
69
+ title={t('settings:version.licenses', { defaultValue: 'Licenses' })}
70
+ titleStyle={this.styles.listItemTitle}
71
+ >
72
+ <View style={this.styles.licenseContent}>
73
+ {legalNotices.noticeText && (
74
+ <Text selectable style={this.styles.licenseText}>
75
+ {legalNotices.noticeText}
76
+ </Text>
77
+ )}
78
+ <Text selectable style={this.styles.licenseText}>
79
+ {legalNotices.licenseText}
80
+ </Text>
81
+ </View>
82
+ </List.Accordion>
83
+ )}
84
+ </List.Section>
67
85
 
68
- {
69
- appSettings.infoBanner &&
70
- <TouchableOpacity
71
- onPress={() => Linking.openURL(appSettings.infoBannerUrl)}
72
- activeOpacity={0.7}
73
- >
74
- <Image
75
- style={this.styles.infoBanner}
76
- source={appSettings.infoBanner}
77
- resizeMode="contain" />
78
- </TouchableOpacity>
79
- }
80
- </View>
81
- </SafeAreaView>
82
- );
83
- }
86
+ {appSettings.infoBanner && (
87
+ <TouchableOpacity
88
+ onPress={() => Linking.openURL(appSettings.infoBannerUrl)}
89
+ activeOpacity={0.7}
90
+ >
91
+ <Image
92
+ style={this.styles.infoBanner}
93
+ source={appSettings.infoBanner}
94
+ resizeMode='contain'
95
+ />
96
+ </TouchableOpacity>
97
+ )}
98
+ </ScrollView>
99
+ </View>
100
+ </SafeAreaView>
101
+ )
102
+ }
84
103
  }
85
104
 
86
105
  export default (withTranslation()(withTheme(AppInfoSettingsView)))
@@ -1,18 +1,27 @@
1
- export default function(theme) {
2
- return {
3
- selectOption: {
4
- display: 'flex',
5
- flexDirection: 'row',
6
- justifyContent: 'space-between'
7
- },
8
- listItemTitle: {
9
- fontSize: theme.fontSizes.l
10
- },
11
- listItemDescription: {
12
- fontSize: theme.fontSizes.m
13
- },
14
- infoBanner: {
15
- width: '100%'
16
- }
1
+ export default function (theme) {
2
+ return {
3
+ selectOption: {
4
+ display: 'flex',
5
+ flexDirection: 'row',
6
+ justifyContent: 'space-between'
7
+ },
8
+ listItemTitle: {
9
+ fontSize: theme.fontSizes.l
10
+ },
11
+ listItemDescription: {
12
+ fontSize: theme.fontSizes.m
13
+ },
14
+ licenseContent: {
15
+ paddingHorizontal: 16,
16
+ paddingBottom: 16
17
+ },
18
+ licenseText: {
19
+ fontSize: theme.fontSizes.m,
20
+ color: theme.colors.text,
21
+ marginBottom: 16
22
+ },
23
+ infoBanner: {
24
+ width: '100%'
17
25
  }
26
+ }
18
27
  }
@@ -138,7 +138,7 @@ export default function UserSettingsView(props) {
138
138
  </>
139
139
  : <>
140
140
  <Text>
141
- Sie sind nicht eingeloogt
141
+ {t('user:notLoggedIn')}
142
142
  </Text>
143
143
  <Button
144
144
  onPress={