@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.
Files changed (268) hide show
  1. package/components/AppBar/index.js +84 -0
  2. package/components/AppBar/styles.js +27 -0
  3. package/components/BookDetail/index.js +268 -0
  4. package/components/BookDetail/styles.js +124 -0
  5. package/components/ComponentJobsFilter/index.js +188 -0
  6. package/components/ComponentJobsFilter/styles.js +12 -0
  7. package/components/ConnectivityWarning/index.js +65 -0
  8. package/components/ConnectivityWarning/styles.js +19 -0
  9. package/components/ContactDetail/index.js +232 -0
  10. package/components/ContactDetail/styles.js +32 -0
  11. package/components/CourseDetail/index.js +357 -0
  12. package/components/CourseDetail/styles.js +59 -0
  13. package/components/CourseDetailDialog/index.js +169 -0
  14. package/components/CourseDetailDialog/styles.js +116 -0
  15. package/components/CourseInfo/index.js +219 -0
  16. package/components/CourseInfo/styles.js +40 -0
  17. package/components/DevelopmentDialog/index.js +214 -0
  18. package/components/DevelopmentDialog/styles.js +10 -0
  19. package/components/EventCodeInput/index.js +149 -0
  20. package/components/EventCodeInput/styles.js +108 -0
  21. package/components/FlexMenuEntry/index.js +84 -0
  22. package/components/FlexMenuEntry/styles.js +27 -0
  23. package/components/MainMenuEntry/index.js +88 -0
  24. package/components/MainMenuEntry/styles.js +28 -0
  25. package/components/MealItem/index.js +87 -0
  26. package/components/MealItem/styles.js +84 -0
  27. package/components/MensaMenu/index.js +305 -0
  28. package/components/MensaMenu/styles.js +94 -0
  29. package/components/MensaSlider/index.js +188 -0
  30. package/components/MensaSlider/styles.js +53 -0
  31. package/components/Modal/index.js +106 -0
  32. package/components/Modal/styles.js +8 -0
  33. package/components/NewsDetail/index.js +377 -0
  34. package/components/NewsDetail/styles.js +77 -0
  35. package/components/NewsList/index.js +120 -0
  36. package/components/NewsList/styles.js +19 -0
  37. package/components/NewsListItem/index.js +89 -0
  38. package/components/NewsListItem/styles.js +32 -0
  39. package/components/OtherCourses/index.js +152 -0
  40. package/components/OtherCourses/styles.js +10 -0
  41. package/components/PtsDeparture/index.js +140 -0
  42. package/components/PtsDeparture/styles.js +7 -0
  43. package/components/PtsStation/index.js +186 -0
  44. package/components/PtsStation/styles.js +47 -0
  45. package/components/QuickLinks/index.js +127 -0
  46. package/components/QuickLinks/styles.js +45 -0
  47. package/components/RoomDetail/index.js +281 -0
  48. package/components/RoomDetail/styles.js +56 -0
  49. package/components/ScaledImage/index.js +92 -0
  50. package/components/SearchResults/index.js +362 -0
  51. package/components/SearchResults/styles.js +59 -0
  52. package/components/SettingSection/index.js +54 -0
  53. package/components/SettingSection/styles.js +15 -0
  54. package/components/SettingsDialog/index.js +52 -0
  55. package/components/SettingsDialog/styles.js +12 -0
  56. package/components/SettingsDialogRadio/index.js +66 -0
  57. package/components/SettingsDialogRadio/styles.js +7 -0
  58. package/components/SettingsDialogSelect/index.js +73 -0
  59. package/components/SettingsDialogSelect/styles.js +7 -0
  60. package/components/TimetableCodeInput/index.js +205 -0
  61. package/components/TimetableCodeInput/styles.js +28 -0
  62. package/components/TimetableDay/index.js +252 -0
  63. package/components/TimetableDay/styles.js +103 -0
  64. package/components/TimetableEvent/index.js +163 -0
  65. package/components/TimetableEvent/styles.js +108 -0
  66. package/components/TimetableList/index.js +180 -0
  67. package/components/TimetableList/styles.js +106 -0
  68. package/components/TimetableMonth/index.js +156 -0
  69. package/components/TimetableMonth/styles.js +29 -0
  70. package/components/TimetableWeek/index.js +230 -0
  71. package/components/TimetableWeek/styles.js +58 -0
  72. package/components/TopNews/index.js +282 -0
  73. package/components/TopNews/styles.js +125 -0
  74. package/components/TopNewsHtwk/index.js +279 -0
  75. package/components/TopNewsHtwk/styles.js +142 -0
  76. package/components/WebView/index.js +108 -0
  77. package/components/WebView/styles.js +11 -0
  78. package/context/context-callmanager/TucCallManager.js +74 -0
  79. package/context/context-callmanager/index.js +128 -0
  80. package/context/context-canteen/AsistServerApiProvider.js +126 -0
  81. package/context/context-canteen/CollektorVersion2ApiProvider.js +100 -0
  82. package/context/context-canteen/index.js +692 -0
  83. package/context/context-connectivity/index.js +85 -0
  84. package/context/context-event/AsistServerClient.js +82 -0
  85. package/context/context-event/index.js +205 -0
  86. package/context/context-flex-menu/AsistServerApiProvider.js +0 -0
  87. package/context/context-flex-menu/CollectorVersion2ApiProvider.js +32 -0
  88. package/context/context-flex-menu/index.js +160 -0
  89. package/context/context-info-dialog/CollektorVersion2ApiProvider.js +53 -0
  90. package/context/context-info-dialog/ProviderErrors.js +22 -0
  91. package/context/context-info-dialog/index.js +190 -0
  92. package/context/context-mails/MailService.js +147 -0
  93. package/context/context-mails/TucUnseenApiProvider.js +65 -0
  94. package/context/context-mails/index.js +131 -0
  95. package/context/context-news/CollektorVersion2ApiProvider.js +68 -0
  96. package/context/context-news/index.js +155 -0
  97. package/context/context-public-transport-ticket/CollektorVersion2ApiProvider.js +45 -0
  98. package/context/context-public-transport-ticket/ProviderErrors.js +11 -0
  99. package/context/context-public-transport-ticket/index.js +175 -0
  100. package/context/context-timetable/CollectorVersion2ApiProvider.js +102 -0
  101. package/context/context-timetable/ProviderErrors.js +22 -0
  102. package/context/context-timetable/TimetableService.js +357 -0
  103. package/context/context-timetable/index.js +215 -0
  104. package/context/context-user/index.js +586 -0
  105. package/index.js +178 -0
  106. package/libraries/base-api-provider/hooks.js +24 -0
  107. package/libraries/base-api-provider/http-api-provider.js +168 -0
  108. package/libraries/base-api-provider/index.js +20 -0
  109. package/libraries/core/api/canteens.js +88 -0
  110. package/libraries/core/api/corona.js +45 -0
  111. package/libraries/core/api/courses.js +54 -0
  112. package/libraries/core/api/device.js +98 -0
  113. package/libraries/core/api/feed.js +144 -0
  114. package/libraries/core/api/feedback.js +123 -0
  115. package/libraries/core/api/jobs.js +48 -0
  116. package/libraries/core/api/library.js +67 -0
  117. package/libraries/core/api/lostfound.js +77 -0
  118. package/libraries/core/api/occupancy.js +44 -0
  119. package/libraries/core/api/officehours.js +25 -0
  120. package/libraries/core/api/poi.js +25 -0
  121. package/libraries/core/api/pts.js +68 -0
  122. package/libraries/core/api/rooms.js +31 -0
  123. package/libraries/core/api/survey.js +121 -0
  124. package/libraries/core/api/telephone.js +32 -0
  125. package/libraries/core/api/tucrooms.js +31 -0
  126. package/libraries/core/constants/actions.js +75 -0
  127. package/libraries/core/constants/api.js +146 -0
  128. package/libraries/core/constants/enums.js +5 -0
  129. package/libraries/core/helper/date.js +9 -0
  130. package/libraries/core/helper/format.helper.js +2 -0
  131. package/libraries/core/index.js +60 -0
  132. package/libraries/core/redux/actions/notifications.js +6 -0
  133. package/libraries/core/redux/actions/setting.js +75 -0
  134. package/libraries/core/redux/actions/state.js +74 -0
  135. package/libraries/core/redux/asyncDispatch.js +27 -0
  136. package/libraries/core/redux/reducers/api.js +190 -0
  137. package/libraries/core/redux/reducers/index.js +32 -0
  138. package/libraries/core/redux/reducers/notifications.js +16 -0
  139. package/libraries/core/redux/reducers/setting.js +99 -0
  140. package/libraries/core/redux/reducers/state.js +122 -0
  141. package/libraries/core/redux/selectors.js +123 -0
  142. package/libraries/core/redux/store.js +40 -0
  143. package/libraries/core/services/data.js +96 -0
  144. package/libraries/core/services/root-navigation.js +36 -0
  145. package/libraries/core/services/search.js +175 -0
  146. package/libraries/core-plugins/enums/replaceTypes.js +53 -0
  147. package/libraries/core-plugins/index.js +21 -0
  148. package/libraries/core-plugins/redux/actions/plugin.js +13 -0
  149. package/libraries/core-plugins/redux/reducers/plugin.js +210 -0
  150. package/libraries/icons-openasist/index.js +143 -0
  151. package/libraries/notifications/index.js +268 -0
  152. package/libraries/notifications/services/subscriber.service.js +123 -0
  153. package/libraries/react-native-webview-autoheight/README.md +47 -0
  154. package/libraries/react-native-webview-autoheight/index.js +140 -0
  155. package/libraries/react-native-webview-autoheight/package.json +37 -0
  156. package/libraries/react-native-webview-autoheight/utils.js +268 -0
  157. package/libraries/redux-offline/.eslintignore +1 -0
  158. package/libraries/redux-offline/.github/ISSUE_TEMPLATE/redux-offline-core-issues-template.md +25 -0
  159. package/libraries/redux-offline/.prettierrc +6 -0
  160. package/libraries/redux-offline/LICENSE +21 -0
  161. package/libraries/redux-offline/README.md +133 -0
  162. package/libraries/redux-offline/flow-typed/npm/redux-logger_vx.x.x.js +109 -0
  163. package/libraries/redux-offline/flow-typed/npm/redux_v4.x.x.js +100 -0
  164. package/libraries/redux-offline/lib/actions.js +46 -0
  165. package/libraries/redux-offline/lib/config.js +15 -0
  166. package/libraries/redux-offline/lib/constants.js +17 -0
  167. package/libraries/redux-offline/lib/defaults/defaultCommit.js +11 -0
  168. package/libraries/redux-offline/lib/defaults/defaultRollback.js +11 -0
  169. package/libraries/redux-offline/lib/defaults/detectNetwork.js +33 -0
  170. package/libraries/redux-offline/lib/defaults/detectNetwork.native.js +211 -0
  171. package/libraries/redux-offline/lib/defaults/discard.js +18 -0
  172. package/libraries/redux-offline/lib/defaults/effect.js +87 -0
  173. package/libraries/redux-offline/lib/defaults/index.js +32 -0
  174. package/libraries/redux-offline/lib/defaults/offlineStateLens.js +24 -0
  175. package/libraries/redux-offline/lib/defaults/persist.js +9 -0
  176. package/libraries/redux-offline/lib/defaults/persist.native.js +17 -0
  177. package/libraries/redux-offline/lib/defaults/persistAutoRehydrate.js +8 -0
  178. package/libraries/redux-offline/lib/defaults/queue.js +27 -0
  179. package/libraries/redux-offline/lib/defaults/retry.js +29 -0
  180. package/libraries/redux-offline/lib/index.js +115 -0
  181. package/libraries/redux-offline/lib/middleware.js +56 -0
  182. package/libraries/redux-offline/lib/offlineActionTracker.js +43 -0
  183. package/libraries/redux-offline/lib/send.js +112 -0
  184. package/libraries/redux-offline/lib/types.js +3 -0
  185. package/libraries/redux-offline/lib/updater.js +111 -0
  186. package/libraries/redux-offline/package.json +69 -0
  187. package/libraries/redux-offline/src/__tests__/defaults/discard.js +22 -0
  188. package/libraries/redux-offline/src/__tests__/defaults/effect.js +118 -0
  189. package/libraries/redux-offline/src/__tests__/index.js +116 -0
  190. package/libraries/redux-offline/src/__tests__/middleware.js +185 -0
  191. package/libraries/redux-offline/src/__tests__/offlineActionTracker.js +54 -0
  192. package/libraries/redux-offline/src/__tests__/send.js +208 -0
  193. package/libraries/redux-offline/src/actions.js +36 -0
  194. package/libraries/redux-offline/src/config.js +9 -0
  195. package/libraries/redux-offline/src/constants.js +18 -0
  196. package/libraries/redux-offline/src/defaults/defaultCommit.js +7 -0
  197. package/libraries/redux-offline/src/defaults/defaultRollback.js +7 -0
  198. package/libraries/redux-offline/src/defaults/detectNetwork.js +16 -0
  199. package/libraries/redux-offline/src/defaults/detectNetwork.native.js +148 -0
  200. package/libraries/redux-offline/src/defaults/discard.js +19 -0
  201. package/libraries/redux-offline/src/defaults/effect.js +86 -0
  202. package/libraries/redux-offline/src/defaults/index.js +25 -0
  203. package/libraries/redux-offline/src/defaults/offlineStateLens.js +11 -0
  204. package/libraries/redux-offline/src/defaults/persist.js +4 -0
  205. package/libraries/redux-offline/src/defaults/persist.native.js +9 -0
  206. package/libraries/redux-offline/src/defaults/persistAutoRehydrate.js +4 -0
  207. package/libraries/redux-offline/src/defaults/queue.js +20 -0
  208. package/libraries/redux-offline/src/defaults/retry.js +18 -0
  209. package/libraries/redux-offline/src/index.js +138 -0
  210. package/libraries/redux-offline/src/middleware.js +52 -0
  211. package/libraries/redux-offline/src/offlineActionTracker.js +40 -0
  212. package/libraries/redux-offline/src/send.js +111 -0
  213. package/libraries/redux-offline/src/types.js +127 -0
  214. package/libraries/redux-offline/src/updater.js +141 -0
  215. package/libraries/redux-offline/typings.d.ts +150 -0
  216. package/libraries/redux-offline/yarn.lock +6460 -0
  217. package/libraries/stored-state/index.js +155 -0
  218. package/package.json +68 -0
  219. package/views/Callmanager/index.js +148 -0
  220. package/views/Callmanager/styles.js +62 -0
  221. package/views/Canteens/index.js +383 -0
  222. package/views/Canteens/styles.js +89 -0
  223. package/views/Dashboard/index.js +176 -0
  224. package/views/Dashboard/styles.js +16 -0
  225. package/views/DashboardHtwk/index.js +197 -0
  226. package/views/DashboardHtwk/styles.js +24 -0
  227. package/views/Event/index.js +373 -0
  228. package/views/Event/styles.js +63 -0
  229. package/views/FeedList/index.js +165 -0
  230. package/views/FeedList/styles.js +15 -0
  231. package/views/FeedNews/index.js +243 -0
  232. package/views/FeedNews/styles.js +32 -0
  233. package/views/Flexmenu/index.js +185 -0
  234. package/views/Flexmenu/styles.js +62 -0
  235. package/views/FlexmenuWebview/index.js +167 -0
  236. package/views/FlexmenuWebview/styles.js +16 -0
  237. package/views/Howhy/index.js +151 -0
  238. package/views/Howhy/styles.js +11 -0
  239. package/views/Jobs/index.js +217 -0
  240. package/views/Jobs/styles.js +41 -0
  241. package/views/MainMenu/index.js +283 -0
  242. package/views/MainMenu/styles.js +36 -0
  243. package/views/NewsTabbar/index.js +164 -0
  244. package/views/NewsTabbar/styles.js +27 -0
  245. package/views/Opal/index.js +136 -0
  246. package/views/Opal/styles.js +11 -0
  247. package/views/PublicTransportTicket/index.js +173 -0
  248. package/views/PublicTransportTicket/styles.js +17 -0
  249. package/views/Search/index.js +186 -0
  250. package/views/Search/styles.js +67 -0
  251. package/views/SettingsAccessibility/index.js +148 -0
  252. package/views/SettingsAccessibility/styles.js +13 -0
  253. package/views/SettingsAppInfo/index.js +86 -0
  254. package/views/SettingsAppInfo/styles.js +18 -0
  255. package/views/SettingsCanteens/index.js +249 -0
  256. package/views/SettingsCanteens/styles.js +33 -0
  257. package/views/SettingsGeneral/index.js +173 -0
  258. package/views/SettingsGeneral/styles.js +28 -0
  259. package/views/SettingsNotifications/index.js +204 -0
  260. package/views/SettingsNotifications/styles.js +38 -0
  261. package/views/SettingsUser/index.js +170 -0
  262. package/views/SettingsUser/styles.js +17 -0
  263. package/views/TimetableCalendar/index.js +387 -0
  264. package/views/TimetableCalendar/styles.js +121 -0
  265. package/views/TimetableList/index.js +402 -0
  266. package/views/TimetableList/styles.js +55 -0
  267. package/views/Webviews/index.js +158 -0
  268. 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
+ }