@olea-bps/base 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/components/AppBar/index.js +84 -0
- package/components/AppBar/styles.js +27 -0
- package/components/BookDetail/index.js +268 -0
- package/components/BookDetail/styles.js +124 -0
- package/components/ComponentJobsFilter/index.js +188 -0
- package/components/ComponentJobsFilter/styles.js +12 -0
- package/components/ConnectivityWarning/index.js +65 -0
- package/components/ConnectivityWarning/styles.js +19 -0
- package/components/ContactDetail/index.js +232 -0
- package/components/ContactDetail/styles.js +32 -0
- package/components/CourseDetail/index.js +357 -0
- package/components/CourseDetail/styles.js +59 -0
- package/components/CourseDetailDialog/index.js +169 -0
- package/components/CourseDetailDialog/styles.js +116 -0
- package/components/CourseInfo/index.js +219 -0
- package/components/CourseInfo/styles.js +40 -0
- package/components/DevelopmentDialog/index.js +214 -0
- package/components/DevelopmentDialog/styles.js +10 -0
- package/components/EventCodeInput/index.js +149 -0
- package/components/EventCodeInput/styles.js +108 -0
- package/components/FlexMenuEntry/index.js +84 -0
- package/components/FlexMenuEntry/styles.js +27 -0
- package/components/MainMenuEntry/index.js +88 -0
- package/components/MainMenuEntry/styles.js +28 -0
- package/components/MealItem/index.js +87 -0
- package/components/MealItem/styles.js +84 -0
- package/components/MensaMenu/index.js +305 -0
- package/components/MensaMenu/styles.js +94 -0
- package/components/MensaSlider/index.js +188 -0
- package/components/MensaSlider/styles.js +53 -0
- package/components/Modal/index.js +106 -0
- package/components/Modal/styles.js +8 -0
- package/components/NewsDetail/index.js +377 -0
- package/components/NewsDetail/styles.js +77 -0
- package/components/NewsList/index.js +120 -0
- package/components/NewsList/styles.js +19 -0
- package/components/NewsListItem/index.js +89 -0
- package/components/NewsListItem/styles.js +32 -0
- package/components/OtherCourses/index.js +152 -0
- package/components/OtherCourses/styles.js +10 -0
- package/components/PtsDeparture/index.js +140 -0
- package/components/PtsDeparture/styles.js +7 -0
- package/components/PtsStation/index.js +186 -0
- package/components/PtsStation/styles.js +47 -0
- package/components/QuickLinks/index.js +127 -0
- package/components/QuickLinks/styles.js +45 -0
- package/components/RoomDetail/index.js +281 -0
- package/components/RoomDetail/styles.js +56 -0
- package/components/ScaledImage/index.js +92 -0
- package/components/SearchResults/index.js +362 -0
- package/components/SearchResults/styles.js +59 -0
- package/components/SettingSection/index.js +54 -0
- package/components/SettingSection/styles.js +15 -0
- package/components/SettingsDialog/index.js +52 -0
- package/components/SettingsDialog/styles.js +12 -0
- package/components/SettingsDialogRadio/index.js +66 -0
- package/components/SettingsDialogRadio/styles.js +7 -0
- package/components/SettingsDialogSelect/index.js +73 -0
- package/components/SettingsDialogSelect/styles.js +7 -0
- package/components/TimetableCodeInput/index.js +205 -0
- package/components/TimetableCodeInput/styles.js +28 -0
- package/components/TimetableDay/index.js +252 -0
- package/components/TimetableDay/styles.js +103 -0
- package/components/TimetableEvent/index.js +163 -0
- package/components/TimetableEvent/styles.js +108 -0
- package/components/TimetableList/index.js +180 -0
- package/components/TimetableList/styles.js +106 -0
- package/components/TimetableMonth/index.js +156 -0
- package/components/TimetableMonth/styles.js +29 -0
- package/components/TimetableWeek/index.js +230 -0
- package/components/TimetableWeek/styles.js +58 -0
- package/components/TopNews/index.js +282 -0
- package/components/TopNews/styles.js +125 -0
- package/components/TopNewsHtwk/index.js +279 -0
- package/components/TopNewsHtwk/styles.js +142 -0
- package/components/WebView/index.js +108 -0
- package/components/WebView/styles.js +11 -0
- package/context/context-callmanager/TucCallManager.js +74 -0
- package/context/context-callmanager/index.js +128 -0
- package/context/context-canteen/AsistServerApiProvider.js +126 -0
- package/context/context-canteen/CollektorVersion2ApiProvider.js +100 -0
- package/context/context-canteen/index.js +692 -0
- package/context/context-connectivity/index.js +85 -0
- package/context/context-event/AsistServerClient.js +82 -0
- package/context/context-event/index.js +205 -0
- package/context/context-flex-menu/AsistServerApiProvider.js +0 -0
- package/context/context-flex-menu/CollectorVersion2ApiProvider.js +32 -0
- package/context/context-flex-menu/index.js +160 -0
- package/context/context-info-dialog/CollektorVersion2ApiProvider.js +53 -0
- package/context/context-info-dialog/ProviderErrors.js +22 -0
- package/context/context-info-dialog/index.js +190 -0
- package/context/context-mails/MailService.js +147 -0
- package/context/context-mails/TucUnseenApiProvider.js +65 -0
- package/context/context-mails/index.js +131 -0
- package/context/context-news/CollektorVersion2ApiProvider.js +68 -0
- package/context/context-news/index.js +155 -0
- package/context/context-public-transport-ticket/CollektorVersion2ApiProvider.js +45 -0
- package/context/context-public-transport-ticket/ProviderErrors.js +11 -0
- package/context/context-public-transport-ticket/index.js +175 -0
- package/context/context-timetable/CollectorVersion2ApiProvider.js +102 -0
- package/context/context-timetable/ProviderErrors.js +22 -0
- package/context/context-timetable/TimetableService.js +357 -0
- package/context/context-timetable/index.js +215 -0
- package/context/context-user/index.js +586 -0
- package/index.js +178 -0
- package/libraries/base-api-provider/hooks.js +24 -0
- package/libraries/base-api-provider/http-api-provider.js +168 -0
- package/libraries/base-api-provider/index.js +20 -0
- package/libraries/core/api/canteens.js +88 -0
- package/libraries/core/api/corona.js +45 -0
- package/libraries/core/api/courses.js +54 -0
- package/libraries/core/api/device.js +98 -0
- package/libraries/core/api/feed.js +144 -0
- package/libraries/core/api/feedback.js +123 -0
- package/libraries/core/api/jobs.js +48 -0
- package/libraries/core/api/library.js +67 -0
- package/libraries/core/api/lostfound.js +77 -0
- package/libraries/core/api/occupancy.js +44 -0
- package/libraries/core/api/officehours.js +25 -0
- package/libraries/core/api/poi.js +25 -0
- package/libraries/core/api/pts.js +68 -0
- package/libraries/core/api/rooms.js +31 -0
- package/libraries/core/api/survey.js +121 -0
- package/libraries/core/api/telephone.js +32 -0
- package/libraries/core/api/tucrooms.js +31 -0
- package/libraries/core/constants/actions.js +75 -0
- package/libraries/core/constants/api.js +146 -0
- package/libraries/core/constants/enums.js +5 -0
- package/libraries/core/helper/date.js +9 -0
- package/libraries/core/helper/format.helper.js +2 -0
- package/libraries/core/index.js +60 -0
- package/libraries/core/redux/actions/notifications.js +6 -0
- package/libraries/core/redux/actions/setting.js +75 -0
- package/libraries/core/redux/actions/state.js +74 -0
- package/libraries/core/redux/asyncDispatch.js +27 -0
- package/libraries/core/redux/reducers/api.js +190 -0
- package/libraries/core/redux/reducers/index.js +32 -0
- package/libraries/core/redux/reducers/notifications.js +16 -0
- package/libraries/core/redux/reducers/setting.js +99 -0
- package/libraries/core/redux/reducers/state.js +122 -0
- package/libraries/core/redux/selectors.js +123 -0
- package/libraries/core/redux/store.js +40 -0
- package/libraries/core/services/data.js +96 -0
- package/libraries/core/services/root-navigation.js +36 -0
- package/libraries/core/services/search.js +175 -0
- package/libraries/core-plugins/enums/replaceTypes.js +53 -0
- package/libraries/core-plugins/index.js +21 -0
- package/libraries/core-plugins/redux/actions/plugin.js +13 -0
- package/libraries/core-plugins/redux/reducers/plugin.js +210 -0
- package/libraries/icons-openasist/index.js +143 -0
- package/libraries/notifications/index.js +268 -0
- package/libraries/notifications/services/subscriber.service.js +123 -0
- package/libraries/react-native-webview-autoheight/README.md +47 -0
- package/libraries/react-native-webview-autoheight/index.js +140 -0
- package/libraries/react-native-webview-autoheight/package.json +37 -0
- package/libraries/react-native-webview-autoheight/utils.js +268 -0
- package/libraries/redux-offline/.eslintignore +1 -0
- package/libraries/redux-offline/.github/ISSUE_TEMPLATE/redux-offline-core-issues-template.md +25 -0
- package/libraries/redux-offline/.prettierrc +6 -0
- package/libraries/redux-offline/LICENSE +21 -0
- package/libraries/redux-offline/README.md +133 -0
- package/libraries/redux-offline/flow-typed/npm/redux-logger_vx.x.x.js +109 -0
- package/libraries/redux-offline/flow-typed/npm/redux_v4.x.x.js +100 -0
- package/libraries/redux-offline/lib/actions.js +46 -0
- package/libraries/redux-offline/lib/config.js +15 -0
- package/libraries/redux-offline/lib/constants.js +17 -0
- package/libraries/redux-offline/lib/defaults/defaultCommit.js +11 -0
- package/libraries/redux-offline/lib/defaults/defaultRollback.js +11 -0
- package/libraries/redux-offline/lib/defaults/detectNetwork.js +33 -0
- package/libraries/redux-offline/lib/defaults/detectNetwork.native.js +211 -0
- package/libraries/redux-offline/lib/defaults/discard.js +18 -0
- package/libraries/redux-offline/lib/defaults/effect.js +87 -0
- package/libraries/redux-offline/lib/defaults/index.js +32 -0
- package/libraries/redux-offline/lib/defaults/offlineStateLens.js +24 -0
- package/libraries/redux-offline/lib/defaults/persist.js +9 -0
- package/libraries/redux-offline/lib/defaults/persist.native.js +17 -0
- package/libraries/redux-offline/lib/defaults/persistAutoRehydrate.js +8 -0
- package/libraries/redux-offline/lib/defaults/queue.js +27 -0
- package/libraries/redux-offline/lib/defaults/retry.js +29 -0
- package/libraries/redux-offline/lib/index.js +115 -0
- package/libraries/redux-offline/lib/middleware.js +56 -0
- package/libraries/redux-offline/lib/offlineActionTracker.js +43 -0
- package/libraries/redux-offline/lib/send.js +112 -0
- package/libraries/redux-offline/lib/types.js +3 -0
- package/libraries/redux-offline/lib/updater.js +111 -0
- package/libraries/redux-offline/package.json +69 -0
- package/libraries/redux-offline/src/__tests__/defaults/discard.js +22 -0
- package/libraries/redux-offline/src/__tests__/defaults/effect.js +118 -0
- package/libraries/redux-offline/src/__tests__/index.js +116 -0
- package/libraries/redux-offline/src/__tests__/middleware.js +185 -0
- package/libraries/redux-offline/src/__tests__/offlineActionTracker.js +54 -0
- package/libraries/redux-offline/src/__tests__/send.js +208 -0
- package/libraries/redux-offline/src/actions.js +36 -0
- package/libraries/redux-offline/src/config.js +9 -0
- package/libraries/redux-offline/src/constants.js +18 -0
- package/libraries/redux-offline/src/defaults/defaultCommit.js +7 -0
- package/libraries/redux-offline/src/defaults/defaultRollback.js +7 -0
- package/libraries/redux-offline/src/defaults/detectNetwork.js +16 -0
- package/libraries/redux-offline/src/defaults/detectNetwork.native.js +148 -0
- package/libraries/redux-offline/src/defaults/discard.js +19 -0
- package/libraries/redux-offline/src/defaults/effect.js +86 -0
- package/libraries/redux-offline/src/defaults/index.js +25 -0
- package/libraries/redux-offline/src/defaults/offlineStateLens.js +11 -0
- package/libraries/redux-offline/src/defaults/persist.js +4 -0
- package/libraries/redux-offline/src/defaults/persist.native.js +9 -0
- package/libraries/redux-offline/src/defaults/persistAutoRehydrate.js +4 -0
- package/libraries/redux-offline/src/defaults/queue.js +20 -0
- package/libraries/redux-offline/src/defaults/retry.js +18 -0
- package/libraries/redux-offline/src/index.js +138 -0
- package/libraries/redux-offline/src/middleware.js +52 -0
- package/libraries/redux-offline/src/offlineActionTracker.js +40 -0
- package/libraries/redux-offline/src/send.js +111 -0
- package/libraries/redux-offline/src/types.js +127 -0
- package/libraries/redux-offline/src/updater.js +141 -0
- package/libraries/redux-offline/typings.d.ts +150 -0
- package/libraries/redux-offline/yarn.lock +6460 -0
- package/libraries/stored-state/index.js +155 -0
- package/package.json +68 -0
- package/views/Callmanager/index.js +148 -0
- package/views/Callmanager/styles.js +62 -0
- package/views/Canteens/index.js +383 -0
- package/views/Canteens/styles.js +89 -0
- package/views/Dashboard/index.js +176 -0
- package/views/Dashboard/styles.js +16 -0
- package/views/DashboardHtwk/index.js +197 -0
- package/views/DashboardHtwk/styles.js +24 -0
- package/views/Event/index.js +373 -0
- package/views/Event/styles.js +63 -0
- package/views/FeedList/index.js +165 -0
- package/views/FeedList/styles.js +15 -0
- package/views/FeedNews/index.js +243 -0
- package/views/FeedNews/styles.js +32 -0
- package/views/Flexmenu/index.js +185 -0
- package/views/Flexmenu/styles.js +62 -0
- package/views/FlexmenuWebview/index.js +167 -0
- package/views/FlexmenuWebview/styles.js +16 -0
- package/views/Howhy/index.js +151 -0
- package/views/Howhy/styles.js +11 -0
- package/views/Jobs/index.js +217 -0
- package/views/Jobs/styles.js +41 -0
- package/views/MainMenu/index.js +283 -0
- package/views/MainMenu/styles.js +36 -0
- package/views/NewsTabbar/index.js +164 -0
- package/views/NewsTabbar/styles.js +27 -0
- package/views/Opal/index.js +136 -0
- package/views/Opal/styles.js +11 -0
- package/views/PublicTransportTicket/index.js +173 -0
- package/views/PublicTransportTicket/styles.js +17 -0
- package/views/Search/index.js +186 -0
- package/views/Search/styles.js +67 -0
- package/views/SettingsAccessibility/index.js +148 -0
- package/views/SettingsAccessibility/styles.js +13 -0
- package/views/SettingsAppInfo/index.js +86 -0
- package/views/SettingsAppInfo/styles.js +18 -0
- package/views/SettingsCanteens/index.js +249 -0
- package/views/SettingsCanteens/styles.js +33 -0
- package/views/SettingsGeneral/index.js +173 -0
- package/views/SettingsGeneral/styles.js +28 -0
- package/views/SettingsNotifications/index.js +204 -0
- package/views/SettingsNotifications/styles.js +38 -0
- package/views/SettingsUser/index.js +170 -0
- package/views/SettingsUser/styles.js +17 -0
- package/views/TimetableCalendar/index.js +387 -0
- package/views/TimetableCalendar/styles.js +121 -0
- package/views/TimetableList/index.js +402 -0
- package/views/TimetableList/styles.js +55 -0
- package/views/Webviews/index.js +158 -0
- package/views/Webviews/styles.js +11 -0
|
@@ -0,0 +1,586 @@
|
|
|
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 { createContext, useState, useEffect, useContext, useMemo, useReducer, useCallback } from 'react'
|
|
16
|
+
|
|
17
|
+
import { useAutoDiscovery, makeRedirectUri, useAuthRequest, exchangeCodeAsync, refreshAsync } from 'expo-auth-session';
|
|
18
|
+
import * as WebBrowser from 'expo-web-browser';
|
|
19
|
+
import * as SecureStore from 'expo-secure-store';
|
|
20
|
+
|
|
21
|
+
import { useTranslation } from 'react-i18next';
|
|
22
|
+
import { useSelector } from 'react-redux';
|
|
23
|
+
import { useTheme } from 'react-native-paper';
|
|
24
|
+
import notifee, { TriggerType } from '@notifee/react-native';
|
|
25
|
+
import { DateTime } from 'luxon';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} OrganizationalUnit
|
|
29
|
+
* @property {string} name Voller Name der Organisationseinheit/Kostenstelle/Fachbereich
|
|
30
|
+
* @property {string} number Nummer der Organisationseinheit
|
|
31
|
+
* @property {string} short_name Kurzbezeichner der Organisationseinheit
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} User
|
|
36
|
+
* @property {string} sub Nutzername
|
|
37
|
+
* @property {string} name Vorname und Nachname (display name) des Nutzers
|
|
38
|
+
* @property {string} given_name Vorname
|
|
39
|
+
* @property {string} family_name Nachname
|
|
40
|
+
* @property {string} email Email
|
|
41
|
+
* @property {string} phone_number Telefonnummer
|
|
42
|
+
* @property {string[]} member_types Angehörigkeitsstatuse des Nutzers
|
|
43
|
+
* @property {OrganizationalUnit[]} organizational_units Organisationseinheiten/Kostenstellen/Fachbereiche, die der Nutzer angehört
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @typedef {Object} UserContext
|
|
48
|
+
* @property {User?} user Angemeldeter Nutzer oder null, wenn nicht angemeldet
|
|
49
|
+
* @property {string?} accessToken OAuth-Token
|
|
50
|
+
* @property {string?} idToken Token für OpenID-Connect
|
|
51
|
+
* @property {Function?} login Login-Funktion, um den Anmeldungprozzess zu starten.
|
|
52
|
+
* @property {Function?} logout Logout-Funktion, um den Nutzer abzumelden.
|
|
53
|
+
* @property {Number} refreshTokenExpiresAt Unix-Zeitstempel(Sekunden) ab wenn sich neu eingeloogt werden muss.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @callback LogoutEffect
|
|
58
|
+
* @param {User} user
|
|
59
|
+
* @returns {void}
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
class LoginError extends Error {
|
|
63
|
+
constructor(message) {
|
|
64
|
+
super(message);
|
|
65
|
+
this.name = this.constructor.name;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const IdTokenTokenSecureStoreKey = 'user.idToken';
|
|
70
|
+
const AccessTokenTokenSecureStoreKey = 'user.accessToken';
|
|
71
|
+
const ExpiresInTokenSecureStoreKey = 'user.expiresIn';
|
|
72
|
+
const IssuedAtTokenSecureStoreKey = 'user.issuedAt';
|
|
73
|
+
const RefreshTokenTokenSecureStoreKey = 'user.refreshToken';
|
|
74
|
+
const ScopeTokenSecureStoreKey = 'user.scope';
|
|
75
|
+
|
|
76
|
+
const AddLogoutEffectAction = 'add_logout_effect';
|
|
77
|
+
const RemoveLogoutEffectAction = 'remove_logout_effect';
|
|
78
|
+
const ClearLogoutEffectAction = 'clear_logout_effect';
|
|
79
|
+
|
|
80
|
+
const RefreshTokenReqiredNotificationChannelId = 'refresh_token_required';
|
|
81
|
+
const RefreshTokenReqiredNotificationId = 'refresh_token_required';
|
|
82
|
+
const RefreshTokenReqiredNotificationIcon = 'notification_icon';
|
|
83
|
+
|
|
84
|
+
const UserContext = createContext();
|
|
85
|
+
|
|
86
|
+
function useSecureStoredState(key, initialState = null, sensitiv = false) {
|
|
87
|
+
const hookName = useSecureStoredState.name;
|
|
88
|
+
|
|
89
|
+
const [state, setState] = useState(initialState);
|
|
90
|
+
|
|
91
|
+
function setSecureStoredState(value) {
|
|
92
|
+
SecureStore
|
|
93
|
+
.setItemAsync(key, JSON.stringify(value ?? null))
|
|
94
|
+
.then(() => setState(value ?? null))
|
|
95
|
+
.catch(reason => console.error(hookName, ': can`t store secure state: ', reason, 'key:', key, 'value:', sensitiv ? '***sensitiv value***' : value));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function deleteSecureStoredState() {
|
|
99
|
+
SecureStore
|
|
100
|
+
.deleteItemAsync(key)
|
|
101
|
+
.then(() => setState(null))
|
|
102
|
+
.then(() => console.debug(hookName, ':', 'remove secure state', key))
|
|
103
|
+
.catch((reason) => console.debug(hookName, ':', 'can`t remove secure state', key, ':', reason));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
useEffect(
|
|
107
|
+
() => {
|
|
108
|
+
SecureStore.getItemAsync(key)
|
|
109
|
+
.then(secureStateValue => {
|
|
110
|
+
if (secureStateValue !== null) {
|
|
111
|
+
setState(JSON.parse(secureStateValue));
|
|
112
|
+
console.debug(hookName, ':', 'restore secure state', key, '=', sensitiv ? '***sensitiv value***' : secureStateValue);
|
|
113
|
+
} else {
|
|
114
|
+
console.debug(hookName, ':', 'not restore for key', ':', key);
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
.catch(reason => console.error(hookName, ':', 'can not restore secure state', ':', reason, 'key', ':', key));
|
|
118
|
+
}, []
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return [state, setSecureStoredState, deleteSecureStoredState]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Reducer für den logoutEffects-State.
|
|
126
|
+
* @param {LogoutEffect[]} state
|
|
127
|
+
* @param {Object} action
|
|
128
|
+
* @param {string} action.type
|
|
129
|
+
* @param {LogoutEffect} action.logoutEffect
|
|
130
|
+
* @returns {LogoutEffect[]}
|
|
131
|
+
*/
|
|
132
|
+
function LogoutEffectsReducer(state, action) {
|
|
133
|
+
const logoutEffect = action?.logoutEffect;
|
|
134
|
+
|
|
135
|
+
switch (action.type) {
|
|
136
|
+
|
|
137
|
+
// Hinzufügen eines Effektes für den Logout
|
|
138
|
+
case AddLogoutEffectAction: {
|
|
139
|
+
if (logoutEffect) {
|
|
140
|
+
return [...state, logoutEffect];
|
|
141
|
+
} else {
|
|
142
|
+
return state;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Entfernen eines Effektes
|
|
147
|
+
case RemoveLogoutEffectAction: {
|
|
148
|
+
return state?.filter(filteringLogout => filteringLogout !== logoutEffect);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Alle Effekte löschen
|
|
152
|
+
case ClearLogoutEffectAction: {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function selectLanguage(reduxState) {
|
|
159
|
+
return reduxState?.settingReducer?.settings?.settingsGeneral?.language ?? 'de';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Provider für den Nutzer-Kontext, welcher für die Anmeldung/Abmeldung des Nutzer zuständig ist.
|
|
164
|
+
*/
|
|
165
|
+
function UserContextProvider({ children }) {
|
|
166
|
+
WebBrowser.maybeCompleteAuthSession();
|
|
167
|
+
|
|
168
|
+
const componentName = UserContextProvider.name;
|
|
169
|
+
|
|
170
|
+
const { t } = useTranslation();
|
|
171
|
+
const language = useSelector(selectLanguage);
|
|
172
|
+
const theme = useTheme();
|
|
173
|
+
const appSettings = theme?.appSettings;
|
|
174
|
+
const authSettings = appSettings?.auth;
|
|
175
|
+
const authRedirectUriScheme = authSettings?.scheme;
|
|
176
|
+
const clientId = authSettings?.clientId;
|
|
177
|
+
const issuer = authSettings?.issuer;
|
|
178
|
+
const refreshTokenAtPercentage = 0.90;
|
|
179
|
+
const scopes = [
|
|
180
|
+
'openid',
|
|
181
|
+
...(authSettings?.scopes),
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
const discovery = useAutoDiscovery(issuer);
|
|
185
|
+
const [idToken, setIdToken, deleteIdToken] = useSecureStoredState(IdTokenTokenSecureStoreKey, null, true);
|
|
186
|
+
const [accessToken, setAccessToken, deleteAccessToken] = useSecureStoredState(AccessTokenTokenSecureStoreKey, null, true);
|
|
187
|
+
const [refreshToken, setRefreshToken, deleteRefreshToken] = useSecureStoredState(RefreshTokenTokenSecureStoreKey, null, true);
|
|
188
|
+
const [expiresIn, setExpiresIn, deleteExpiresIn] = useSecureStoredState(ExpiresInTokenSecureStoreKey, null);
|
|
189
|
+
const [issuedAt, setIssuedAt, deleteIssuedAt] = useSecureStoredState(IssuedAtTokenSecureStoreKey, null);
|
|
190
|
+
const [user, setUser] = useState(null);
|
|
191
|
+
const [logoutEffects, dispatchLogoutEffects] = useReducer(LogoutEffectsReducer, []);
|
|
192
|
+
|
|
193
|
+
// Errechnen des Zeitpunktes, ab wann der Refresh-Token verfällt
|
|
194
|
+
const refreshTokenExpiresAt = issuedAt && expiresIn
|
|
195
|
+
? issuedAt + expiresIn
|
|
196
|
+
: null;
|
|
197
|
+
|
|
198
|
+
// Errechnen des Zeitpunktes, ab wann das Token erneuert werden soll (Sekunden ab UNIX-Time)
|
|
199
|
+
const refreshTokenAt = issuedAt && expiresIn && refreshTokenAtPercentage
|
|
200
|
+
// Zeitpunkt(Zeitstempel), wann der jetzige Token erstellt wurde + (Zeit bis der Token verfallen ist * Prozentualler Anteil, in welchen der Token vor seinem Verfall erneuert werden soll)
|
|
201
|
+
? issuedAt + (expiresIn * refreshTokenAtPercentage)
|
|
202
|
+
: null;
|
|
203
|
+
|
|
204
|
+
const redirectUri = useMemo(
|
|
205
|
+
() => makeRedirectUri({ scheme: authRedirectUriScheme, path: 'login' }),
|
|
206
|
+
[authRedirectUriScheme]
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
const [request, response, promptAsync] = useAuthRequest(
|
|
210
|
+
{
|
|
211
|
+
clientId: clientId,
|
|
212
|
+
redirectUri: redirectUri,
|
|
213
|
+
scopes: scopes,
|
|
214
|
+
},
|
|
215
|
+
discovery
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
function statesFromTokenResponse(tokenResponse) {
|
|
219
|
+
setIdToken(tokenResponse?.idToken);
|
|
220
|
+
setAccessToken(tokenResponse?.accessToken);
|
|
221
|
+
setExpiresIn(tokenResponse?.expiresIn);
|
|
222
|
+
setIssuedAt(tokenResponse?.issuedAt);
|
|
223
|
+
setRefreshToken(tokenResponse?.refreshToken);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
useEffect(
|
|
227
|
+
() => {
|
|
228
|
+
if (accessToken && discovery?.userInfoEndpoint) {
|
|
229
|
+
fetch(
|
|
230
|
+
discovery.userInfoEndpoint,
|
|
231
|
+
{
|
|
232
|
+
headers: {
|
|
233
|
+
'Authorization': 'Bearer ' + accessToken,
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
).then(
|
|
237
|
+
response => response.json()
|
|
238
|
+
).then(
|
|
239
|
+
userInfoJsonResponse => {
|
|
240
|
+
console.debug(componentName, ':', 'fetch user', ':', userInfoJsonResponse);
|
|
241
|
+
|
|
242
|
+
const userInfos = {
|
|
243
|
+
...userInfoJsonResponse,
|
|
244
|
+
organizational_units: userInfoJsonResponse?.ou?.map(
|
|
245
|
+
(organizational_unit_name, index) => ({
|
|
246
|
+
name: organizational_unit_name,
|
|
247
|
+
number: userInfoJsonResponse?.ounumber?.[index],
|
|
248
|
+
short_name: userInfoJsonResponse?.oushort?.[index],
|
|
249
|
+
})
|
|
250
|
+
) ?? [],
|
|
251
|
+
member_types: [
|
|
252
|
+
...(userInfoJsonResponse?.employeetype?.includes?.('Student') ? ['student'] : null),
|
|
253
|
+
...(userInfoJsonResponse?.employeetype?.includes?.('Mitarbeiter') ? ['employee'] : null),
|
|
254
|
+
]
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
console.debug(componentName, ':', 'convert response to user', ':', userInfos);
|
|
258
|
+
|
|
259
|
+
setUser(userInfos);
|
|
260
|
+
}
|
|
261
|
+
).catch(
|
|
262
|
+
reason => console.warn(componentName, ':', 'can`t fetch user infos', ':', reason)
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
[accessToken, discovery]
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Wenn das Format vom Server stimmt wird kann der oberer Hook mit diesem ersetzt werden
|
|
270
|
+
//useEffect(
|
|
271
|
+
// () => {
|
|
272
|
+
// if (accessToken && discovery) {
|
|
273
|
+
// fetchUserInfoAsync(
|
|
274
|
+
// {
|
|
275
|
+
// accessToken: accessToken
|
|
276
|
+
// },
|
|
277
|
+
// discovery,
|
|
278
|
+
// ).then(
|
|
279
|
+
// userInfo => console.warn(userInfo)
|
|
280
|
+
// ).then(
|
|
281
|
+
// response => response.json()
|
|
282
|
+
// ).then(
|
|
283
|
+
// jsonResponse => {
|
|
284
|
+
// console.debug(componentName, ':', 'fetch user ', jsonResponse);
|
|
285
|
+
// setUser(jsonResponse);
|
|
286
|
+
// }
|
|
287
|
+
// ).catch(
|
|
288
|
+
// reason => console.warn(componentName, ':', 'can`t fetch user infos', ':', reason)
|
|
289
|
+
// );
|
|
290
|
+
// }
|
|
291
|
+
// },
|
|
292
|
+
// [accessToken, discovery]
|
|
293
|
+
//);
|
|
294
|
+
|
|
295
|
+
useEffect(
|
|
296
|
+
() => {
|
|
297
|
+
if (refreshToken && refreshTokenAt && discovery) {
|
|
298
|
+
const refreshTokenAtMillisecond = refreshTokenAt * 1000;
|
|
299
|
+
const now = Date.now()
|
|
300
|
+
const timeout = setTimeout(
|
|
301
|
+
() =>
|
|
302
|
+
refreshAsync(
|
|
303
|
+
{
|
|
304
|
+
clientId, clientId,
|
|
305
|
+
refreshToken: refreshToken,
|
|
306
|
+
},
|
|
307
|
+
discovery,
|
|
308
|
+
).then(
|
|
309
|
+
statesFromTokenResponse
|
|
310
|
+
).catch(
|
|
311
|
+
reason => console.warn(componentName, ':', 'cant`t refresh tokens', ':', reason)
|
|
312
|
+
),
|
|
313
|
+
refreshTokenAtMillisecond - now
|
|
314
|
+
);
|
|
315
|
+
return () => clearTimeout(timeout);
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
[refreshToken, refreshTokenAt, discovery]
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
useEffect(
|
|
322
|
+
() => {
|
|
323
|
+
if (refreshToken && refreshTokenAt && refreshTokenExpiresAt) {
|
|
324
|
+
notifee.requestPermission()
|
|
325
|
+
.then(
|
|
326
|
+
notificationSettings =>
|
|
327
|
+
notifee.createChannel({
|
|
328
|
+
id: RefreshTokenReqiredNotificationChannelId,
|
|
329
|
+
name: t('authentication:notificationChannelName'),
|
|
330
|
+
description: t('authentication:notificationChannelDescription'),
|
|
331
|
+
lights: true,
|
|
332
|
+
sound: 'default',
|
|
333
|
+
})
|
|
334
|
+
).then(
|
|
335
|
+
channelId => {
|
|
336
|
+
const refreshTokenExpiresAtMillisecond = refreshTokenAt * 1000;
|
|
337
|
+
const refreshTokenExpiresAtDateTime = DateTime.fromMillis(refreshTokenExpiresAtMillisecond, { locale: language });
|
|
338
|
+
const refreshTokenExpiresAtDateLocaleString = refreshTokenExpiresAtDateTime.toLocaleString(DateTime.DATE_SHORT);
|
|
339
|
+
const refreshTokenExpiresAtTimeLocaleString = refreshTokenExpiresAtDateTime.toLocaleString(DateTime.TIME_SIMPLE);
|
|
340
|
+
return notifee.createTriggerNotification(
|
|
341
|
+
{
|
|
342
|
+
id: RefreshTokenReqiredNotificationId,
|
|
343
|
+
title: t(
|
|
344
|
+
'authentication:tokenInvalidNotification',
|
|
345
|
+
{
|
|
346
|
+
refreshTokenExpiresAtDate: refreshTokenExpiresAtDateLocaleString,
|
|
347
|
+
refreshTokenExpiresAtTime: refreshTokenExpiresAtTimeLocaleString
|
|
348
|
+
|
|
349
|
+
}
|
|
350
|
+
),
|
|
351
|
+
android: {
|
|
352
|
+
channelId: channelId,
|
|
353
|
+
showChronometer: true,
|
|
354
|
+
chronometerDirection: 'down',
|
|
355
|
+
timestamp: refreshTokenExpiresAt * 1000,
|
|
356
|
+
smallIcon: RefreshTokenReqiredNotificationIcon,
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
type: TriggerType.TIMESTAMP,
|
|
361
|
+
timestamp: refreshTokenExpiresAtMillisecond,
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
)
|
|
366
|
+
} else {
|
|
367
|
+
notifee.cancelDisplayedNotification(RefreshTokenReqiredNotificationId);
|
|
368
|
+
notifee.cancelTriggerNotification(RefreshTokenReqiredNotificationId);
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
[refreshToken && refreshTokenAt, language, t]
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
function login() {
|
|
375
|
+
return promptAsync()
|
|
376
|
+
.then(
|
|
377
|
+
codeResponse => {
|
|
378
|
+
if (codeResponse?.type === 'success') {
|
|
379
|
+
return exchangeCodeAsync(
|
|
380
|
+
{
|
|
381
|
+
clientId,
|
|
382
|
+
code: codeResponse.params.code,
|
|
383
|
+
extraParams:
|
|
384
|
+
request.codeVerifier
|
|
385
|
+
? { code_verifier: request.codeVerifier }
|
|
386
|
+
: undefined,
|
|
387
|
+
redirectUri,
|
|
388
|
+
},
|
|
389
|
+
discovery
|
|
390
|
+
).then(
|
|
391
|
+
statesFromTokenResponse
|
|
392
|
+
).catch(
|
|
393
|
+
reason => console.log(componentName, ': tokenResponse: Fehler', ':', reason)
|
|
394
|
+
);
|
|
395
|
+
} else {
|
|
396
|
+
// TODO: Prüfen warum der Login nicht klappte und für die einzeilnen Gründe, genauere Fehlerinfos zurückgeben.
|
|
397
|
+
throw new LoginError(`Login Failed: ${codeResponse?.type}`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Fügt einen Effekt hinzu, welcher nach dem Logout ausgeführt wird.
|
|
405
|
+
* Diese Funktion sollte nicht verwendet werden, stattdessen ist useLogoutEffect zu benutzen.
|
|
406
|
+
* @function
|
|
407
|
+
* @param {LogoutEffect} effect Callback welche noch dem Logout ausgeführt werden soll
|
|
408
|
+
*/
|
|
409
|
+
const addLogoutListener = useCallback(
|
|
410
|
+
/**
|
|
411
|
+
* @param {LogoutEffect} effect Callback welche noch dem Logout ausgeführt werden soll
|
|
412
|
+
*/
|
|
413
|
+
(effect) =>
|
|
414
|
+
dispatchLogoutEffects(
|
|
415
|
+
{
|
|
416
|
+
type: AddLogoutEffectAction,
|
|
417
|
+
logoutEffect: effect,
|
|
418
|
+
}
|
|
419
|
+
),
|
|
420
|
+
[dispatchLogoutEffects, AddLogoutEffectAction]
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Entfernt einen Effekt aus der Liste der Effekts, welche nach dem Logout ausgeführt werden.
|
|
425
|
+
* Diese Funktion sollte nicht verwendet werden, stattdessen ist useLogoutEffect zu benutzen.
|
|
426
|
+
* @function
|
|
427
|
+
* @param {LogoutEffect} effect Callback, welche entfernt werden soll.
|
|
428
|
+
*/
|
|
429
|
+
const removeLogoutListener = useCallback(
|
|
430
|
+
/**
|
|
431
|
+
* @param {LogoutEffect} effect
|
|
432
|
+
*/
|
|
433
|
+
(effect) =>
|
|
434
|
+
dispatchLogoutEffects(
|
|
435
|
+
{
|
|
436
|
+
type: RemoveLogoutEffectAction,
|
|
437
|
+
logoutEffect: effect,
|
|
438
|
+
}
|
|
439
|
+
),
|
|
440
|
+
[dispatchLogoutEffects, RemoveLogoutEffectAction]
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
function logout() {
|
|
444
|
+
deleteIdToken();
|
|
445
|
+
deleteAccessToken();
|
|
446
|
+
deleteExpiresIn();
|
|
447
|
+
deleteIssuedAt();
|
|
448
|
+
deleteRefreshToken();
|
|
449
|
+
setUser(null);
|
|
450
|
+
|
|
451
|
+
// Nachdem alles aufgeräumt ist, lösen wir die Effekte/Callbacks für den Logout aus
|
|
452
|
+
logoutEffects?.forEach(effect => effect(user));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return (
|
|
456
|
+
<UserContext.Provider
|
|
457
|
+
value={{
|
|
458
|
+
user: user,
|
|
459
|
+
login: request ? login : null,
|
|
460
|
+
logout: idToken !== null || accessToken !== null ? logout : null,
|
|
461
|
+
accessToken: accessToken,
|
|
462
|
+
idToken: idToken,
|
|
463
|
+
addLogoutListener,
|
|
464
|
+
removeLogoutListener,
|
|
465
|
+
refreshTokenExpiresAt,
|
|
466
|
+
}}
|
|
467
|
+
>
|
|
468
|
+
{children}
|
|
469
|
+
</UserContext.Provider>
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Gib den Nutzercontext zurück
|
|
475
|
+
* @returns {UserContext}
|
|
476
|
+
*/
|
|
477
|
+
function useUserContext() {
|
|
478
|
+
return useContext(UserContext);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Gibt den derzeitigen Access-Token des Nutzers zurück. Ist der Nutzer nicht angemeldet wird null zurückgeben.
|
|
483
|
+
* @returns {string|null} Access-Token oder null, wenn Nutzer nicht angemeldet.
|
|
484
|
+
*/
|
|
485
|
+
function useAccessToken() {
|
|
486
|
+
return useUserContext()?.accessToken;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Gibt den derzeitigen ID-Token des Nutzers zurück. Ist der Nutzer nicht angemeldet wird null zurückgeben.
|
|
491
|
+
* @returns {string|null} ID-Token oder null, wenn Nutzer nicht angemeldet.
|
|
492
|
+
*/
|
|
493
|
+
function useIdToken() {
|
|
494
|
+
return useUserContext()?.idToken;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Liefert den Nutzer, die Login-Funktion und die Logout-Funktion zurück.
|
|
499
|
+
*
|
|
500
|
+
* Dieser Hook kann von Modulen genutzt werden, um die Nutzerinformationen zu verwalten.
|
|
501
|
+
* Dabei wird ein Array mit 3 Elementen zurückgegeben.
|
|
502
|
+
*
|
|
503
|
+
* Das erste Element ist der aktuelle eingelogte Nutzer oder null, wenn kein Nutzer eingeloogt ist.
|
|
504
|
+
* Als Zweites wird eine Funktion für den Login zur Verfügung gestellt, welche parameterlos ausgeführt werden kann, um den Login-Prozess zu starten.
|
|
505
|
+
* Das dritte Element ist eine Funktion für die Abmeldung des Nutzers, welche keinen Paremter entgegennimmt.
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* const [user, login, logout] = useUser();
|
|
509
|
+
* if(!user) {
|
|
510
|
+
* login()
|
|
511
|
+
* .then(() => console.debug('Nutzer ist angemeldet'))
|
|
512
|
+
* .catch((reason) => console.debug('Nutzer konnte nicht angemeldet werden:', reason));
|
|
513
|
+
* }
|
|
514
|
+
*
|
|
515
|
+
* return (
|
|
516
|
+
* <Text>
|
|
517
|
+
* {user?.diplayName ?? 'Kein Nutzer verfügbar'}
|
|
518
|
+
* </Text>
|
|
519
|
+
* );
|
|
520
|
+
* @return {[user: User|null, login: () => Promise<void>, logout: () => Promise<void>]}
|
|
521
|
+
* @return {User} user - Das Nutzerobjekt oder null wenn kein Nutzer angemeldet ist
|
|
522
|
+
* @return {() => Promise<void>} login - Login-Funktion, die den Login-Prozess startet
|
|
523
|
+
* @return {() => Promise<void>} logout - Logout-Funtion, welche den Nutzer abmeldet
|
|
524
|
+
*/
|
|
525
|
+
function useUser() {
|
|
526
|
+
const userContext = useUserContext();
|
|
527
|
+
|
|
528
|
+
return [userContext?.user, userContext?.login, userContext?.logout]
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Führt einen Effekt aus, wenn der Nutzer abgemeldet wird.
|
|
533
|
+
* Der effect-Parameter ist eine Callback-Funktion.
|
|
534
|
+
* @param {LogoutEffect} effect
|
|
535
|
+
* @see LogoutEffect
|
|
536
|
+
* @example
|
|
537
|
+
* import { useCallback } from 'react';
|
|
538
|
+
* import { useLogoutEffect } from '@olea-base';
|
|
539
|
+
*
|
|
540
|
+
* function MyComponent() {
|
|
541
|
+
*
|
|
542
|
+
* useLogoutEffect(
|
|
543
|
+
* useCallback(
|
|
544
|
+
* user => {
|
|
545
|
+
* console.log('Nutzer', user.sub, 'erfolgreich abgemeldet.');
|
|
546
|
+
* },
|
|
547
|
+
* []
|
|
548
|
+
* )
|
|
549
|
+
* );
|
|
550
|
+
*
|
|
551
|
+
* }
|
|
552
|
+
*/
|
|
553
|
+
function useLogoutEffect(effect) {
|
|
554
|
+
// Holen der Logout-Listener-Funktionen aus dem Kontext
|
|
555
|
+
const { addLogoutListener, removeLogoutListener } = useUserContext();
|
|
556
|
+
|
|
557
|
+
// Dieser Effekt fügt die Callback-Funktion im effect-Paramter als Listener hinzu.
|
|
558
|
+
// Wird das Modul/der Kontext, der den useLogoutEffect benutzt, nicht mehr benutzt, wird die Callback wieder als Listener entfernt.
|
|
559
|
+
// Auch wenn die Callback-Funktion geändert wird, wird davor die alte Variante als Listner entfernt.
|
|
560
|
+
useEffect(
|
|
561
|
+
() => {
|
|
562
|
+
// Regestrireen der Callback-Funktion als Logout-Effekt beim User-Kontext
|
|
563
|
+
addLogoutListener(effect);
|
|
564
|
+
|
|
565
|
+
// Deregestriern der Callback-Funktion beim User-Kontext
|
|
566
|
+
return () => removeLogoutListener(effect);
|
|
567
|
+
},
|
|
568
|
+
[
|
|
569
|
+
// Falls sich die Callback-Funktion ändert, soll die alte Variante deregestriert werden
|
|
570
|
+
effect,
|
|
571
|
+
// Ändert sich etwas im User-Kontext, wodurch sich addLogoutListener/removeLogoutListener ändert, soll die Callback als der alten Struktur entfernt werden und in der neuen Strucktur initialisert werden.
|
|
572
|
+
addLogoutListener,
|
|
573
|
+
removeLogoutListener
|
|
574
|
+
]
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
export {
|
|
579
|
+
UserContext as default,
|
|
580
|
+
UserContextProvider,
|
|
581
|
+
useUserContext,
|
|
582
|
+
useAccessToken,
|
|
583
|
+
useIdToken,
|
|
584
|
+
useUser,
|
|
585
|
+
useLogoutEffect,
|
|
586
|
+
}
|