@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,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OFFLINE_STATUS_CHANGED,
|
|
3
|
+
OFFLINE_SCHEDULE_RETRY,
|
|
4
|
+
OFFLINE_COMPLETE_RETRY,
|
|
5
|
+
OFFLINE_BUSY
|
|
6
|
+
} from './constants';
|
|
7
|
+
|
|
8
|
+
export const networkStatusChanged = params => {
|
|
9
|
+
let payload;
|
|
10
|
+
if (typeof params === 'object') {
|
|
11
|
+
payload = params;
|
|
12
|
+
} else {
|
|
13
|
+
payload = { online: params };
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
type: OFFLINE_STATUS_CHANGED,
|
|
17
|
+
payload
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const scheduleRetry = (delay = 0) => ({
|
|
22
|
+
type: OFFLINE_SCHEDULE_RETRY,
|
|
23
|
+
payload: {
|
|
24
|
+
delay
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const completeRetry = action => ({
|
|
29
|
+
type: OFFLINE_COMPLETE_RETRY,
|
|
30
|
+
payload: action
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const busy = isBusy => ({
|
|
34
|
+
type: OFFLINE_BUSY,
|
|
35
|
+
payload: { busy: isBusy }
|
|
36
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
// Literal ACTION typing allows to ensure a stricter type than string
|
|
3
|
+
|
|
4
|
+
export const OFFLINE_STATUS_CHANGED: 'Offline/STATUS_CHANGED' =
|
|
5
|
+
'Offline/STATUS_CHANGED';
|
|
6
|
+
export const OFFLINE_SCHEDULE_RETRY: 'Offline/SCHEDULE_RETRY' =
|
|
7
|
+
'Offline/SCHEDULE_RETRY';
|
|
8
|
+
export const OFFLINE_COMPLETE_RETRY: 'Offline/COMPLETE_RETRY' =
|
|
9
|
+
'Offline/COMPLETE_RETRY';
|
|
10
|
+
export const OFFLINE_SEND: 'Offline/SEND' = 'Offline/SEND';
|
|
11
|
+
export const OFFLINE_BUSY: 'Offline/BUSY' = 'Offline/BUSY';
|
|
12
|
+
export const RESET_STATE: 'Offline/RESET_STATE' = 'Offline/RESET_STATE';
|
|
13
|
+
export const PERSIST_REHYDRATE: 'persist/REHYDRATE' = 'persist/REHYDRATE';
|
|
14
|
+
export const JS_ERROR: 'Offline/JS_ERROR' = 'Offline/JS_ERROR';
|
|
15
|
+
export const DEFAULT_COMMIT: 'Offline/DEFAULT_COMMIT' =
|
|
16
|
+
'Offline/DEFAULT_COMMIT';
|
|
17
|
+
export const DEFAULT_ROLLBACK: 'Offline/DEFAULT_ROLLBACK' =
|
|
18
|
+
'Offline/DEFAULT_ROLLBACK';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const handle = (callback, online) => {
|
|
2
|
+
// NetInfo is not supported in browsers, hence we only pass online status
|
|
3
|
+
if (window.requestAnimationFrame) {
|
|
4
|
+
window.requestAnimationFrame(() => callback({ online }));
|
|
5
|
+
} else {
|
|
6
|
+
setTimeout(() => callback({ online }), 0);
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default callback => {
|
|
11
|
+
if (typeof window !== 'undefined' && window.addEventListener) {
|
|
12
|
+
window.addEventListener('online', () => handle(callback, true));
|
|
13
|
+
window.addEventListener('offline', () => handle(callback, false));
|
|
14
|
+
handle(callback, window.navigator.onLine);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/* eslint no-underscore-dangle: 0 */
|
|
2
|
+
import { AppState } from 'react-native'; // eslint-disable-line
|
|
3
|
+
import NetInfo from '@react-native-community/netinfo'; // eslint-disable-line
|
|
4
|
+
|
|
5
|
+
class DetectNetwork {
|
|
6
|
+
constructor(callback) {
|
|
7
|
+
this._reach = null;
|
|
8
|
+
this._isConnected = null;
|
|
9
|
+
this._isConnectionExpensive = null;
|
|
10
|
+
this._callback = callback;
|
|
11
|
+
this._shouldInitUpdateReach = true;
|
|
12
|
+
|
|
13
|
+
this._init();
|
|
14
|
+
this._addListeners();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check props for changes
|
|
19
|
+
* @param {string} reach - connection reachability.
|
|
20
|
+
* - Cross-platform: [none, wifi, cellular, unknown]
|
|
21
|
+
* - Android: [bluetooth, ethernet, wimax]
|
|
22
|
+
* @returns {boolean} - Whether the connection reachability or the connection props have changed
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
_hasChanged = reach => {
|
|
26
|
+
if (this._reach !== reach) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (this._isConnected !== this._getConnection(reach)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Sets the connection reachability prop
|
|
37
|
+
* @param {string} reach - connection reachability.
|
|
38
|
+
* - Cross-platform: [none, wifi, cellular, unknown]
|
|
39
|
+
* - Android: [bluetooth, ethernet, wimax]
|
|
40
|
+
* @returns {void}
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
_setReach = reach => {
|
|
44
|
+
this._reach = reach;
|
|
45
|
+
this._isConnected = this._getConnection(reach);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Gets the isConnected prop depending on the connection reachability's value
|
|
50
|
+
* @param {string} reach - connection reachability.
|
|
51
|
+
* - Cross-platform: [none, wifi, cellular, unknown]
|
|
52
|
+
* - Android: [bluetooth, ethernet, wimax]
|
|
53
|
+
* @returns {void}
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
56
|
+
_getConnection = reach => reach !== 'none' && reach !== 'unknown';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Sets the isConnectionExpensive prop
|
|
60
|
+
* @returns {Promise.<void>} Resolves to true if connection is expensive,
|
|
61
|
+
* false if not, and null if not supported.
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
_setIsConnectionExpensive = async () => {
|
|
65
|
+
try {
|
|
66
|
+
const { details } = await NetInfo.fetch();
|
|
67
|
+
this._isConnectionExpensive = details.isConnectionExpensive;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
// err means that isConnectionExpensive is not supported in iOS
|
|
70
|
+
this._isConnectionExpensive = null;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Sets the shouldInitUpdateReach flag
|
|
76
|
+
* @param {boolean} shouldUpdate - Whether the init method should update the reach prop
|
|
77
|
+
* @returns {void}
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
_setShouldInitUpdateReach = shouldUpdate => {
|
|
81
|
+
this._shouldInitUpdateReach = shouldUpdate;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Fetches and sets the connection reachability and the isConnected props,
|
|
86
|
+
* if neither of the AppState and NetInfo event listeners have been called
|
|
87
|
+
* @returns {Promise.<void>} Resolves when the props have been
|
|
88
|
+
* initialized and update.
|
|
89
|
+
* @private
|
|
90
|
+
*/
|
|
91
|
+
_init = async () => {
|
|
92
|
+
const connectionInfo = await NetInfo.fetch();
|
|
93
|
+
if (this._shouldInitUpdateReach) {
|
|
94
|
+
this._update(connectionInfo.type);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check changes on props and store and dispatch if neccesary
|
|
100
|
+
* @param {string} reach - connection reachability.
|
|
101
|
+
* - Cross-platform: [none, wifi, cellular, unknown]
|
|
102
|
+
* - Android: [bluetooth, ethernet, wimax]
|
|
103
|
+
* @returns {void}
|
|
104
|
+
* @private
|
|
105
|
+
*/
|
|
106
|
+
_update = reach => {
|
|
107
|
+
if (this._hasChanged(reach)) {
|
|
108
|
+
this._setReach(reach);
|
|
109
|
+
this._dispatch();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Adds listeners for when connection reachability and app state changes to update props
|
|
115
|
+
* @returns {void}
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
_addListeners() {
|
|
119
|
+
NetInfo.addEventListener(connectionInfo => {
|
|
120
|
+
this._setShouldInitUpdateReach(false);
|
|
121
|
+
this._update(connectionInfo.type);
|
|
122
|
+
});
|
|
123
|
+
AppState.addEventListener('change', async () => {
|
|
124
|
+
this._setShouldInitUpdateReach(false);
|
|
125
|
+
const connectionInfo = await NetInfo.fetch();
|
|
126
|
+
this._update(connectionInfo.type);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Executes the given callback to update redux's store with the new internal props
|
|
132
|
+
* @returns {Promise.<void>} Resolves after fetching the isConnectionExpensive
|
|
133
|
+
* and dispatches actions
|
|
134
|
+
* @private
|
|
135
|
+
*/
|
|
136
|
+
_dispatch = async () => {
|
|
137
|
+
await this._setIsConnectionExpensive();
|
|
138
|
+
this._callback({
|
|
139
|
+
online: this._isConnected,
|
|
140
|
+
netInfo: {
|
|
141
|
+
isConnectionExpensive: this._isConnectionExpensive,
|
|
142
|
+
reach: this._reach
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export default callback => new DetectNetwork(callback);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import type { OfflineAction } from '../types';
|
|
4
|
+
import { NetworkError } from './effect';
|
|
5
|
+
|
|
6
|
+
export default (
|
|
7
|
+
error: typeof NetworkError,
|
|
8
|
+
action: OfflineAction,
|
|
9
|
+
_retries: number = 0 // eslint-disable-line no-unused-vars
|
|
10
|
+
): boolean => {
|
|
11
|
+
// not a network error -> discard
|
|
12
|
+
if (!('status' in error)) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// discard http 4xx errors
|
|
17
|
+
// $FlowFixMe
|
|
18
|
+
return error.status >= 400 && error.status < 500;
|
|
19
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import type { OfflineAction } from '../types';
|
|
4
|
+
|
|
5
|
+
export function NetworkError(response: {} | string, status: number) {
|
|
6
|
+
this.name = 'NetworkError';
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.response = response;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// $FlowFixMe
|
|
12
|
+
NetworkError.prototype = Error.prototype;
|
|
13
|
+
|
|
14
|
+
const tryParseJSON = (json: string): ?{} => {
|
|
15
|
+
if (!json) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(json);
|
|
20
|
+
} catch (e) {
|
|
21
|
+
throw new Error(`Failed to parse unexpected JSON response: ${json}`);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const getResponseBody = (res: any): Promise<{} | string> => {
|
|
26
|
+
const contentType = res.headers.get('content-type') || false;
|
|
27
|
+
if (contentType && contentType.indexOf('json') >= 0) {
|
|
28
|
+
return res.text().then(tryParseJSON);
|
|
29
|
+
}
|
|
30
|
+
return res.text();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const getHeaders = (headers: {
|
|
34
|
+
[string]: string
|
|
35
|
+
}): { [string]: string } => {
|
|
36
|
+
const {
|
|
37
|
+
'Content-Type': contentTypeCapitalized,
|
|
38
|
+
'content-type': contentTypeLowerCase,
|
|
39
|
+
...restOfHeaders
|
|
40
|
+
} = headers || {};
|
|
41
|
+
const contentType =
|
|
42
|
+
contentTypeCapitalized || contentTypeLowerCase || 'application/json';
|
|
43
|
+
return { ...restOfHeaders, 'content-type': contentType };
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const getFormData = (object: {}) => {
|
|
47
|
+
const formData = new FormData();
|
|
48
|
+
Object.keys(object).forEach(key => {
|
|
49
|
+
Object.keys(object[key]).forEach(innerObj => {
|
|
50
|
+
const newObj = object[key][innerObj];
|
|
51
|
+
formData.append(newObj[0], newObj[1]);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return formData;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// eslint-disable-next-line no-unused-vars
|
|
59
|
+
export default (effect: any, _action: OfflineAction): Promise<any> => {
|
|
60
|
+
const { url, json, ...options } = effect;
|
|
61
|
+
const headers = getHeaders(options.headers);
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
!(options.body instanceof FormData) &&
|
|
65
|
+
Object.prototype.hasOwnProperty.call(headers, 'content-type') &&
|
|
66
|
+
headers['content-type'].toLowerCase().includes('multipart/form-data')
|
|
67
|
+
) {
|
|
68
|
+
options.body = getFormData(options.body);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (json !== null && json !== undefined) {
|
|
72
|
+
try {
|
|
73
|
+
options.body = JSON.stringify(json);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return Promise.reject(e);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return fetch(url, { ...options, headers }).then(res => {
|
|
79
|
+
if (res.ok) {
|
|
80
|
+
return getResponseBody(res);
|
|
81
|
+
}
|
|
82
|
+
return getResponseBody(res).then(body => {
|
|
83
|
+
throw new NetworkError(body || '', res.status);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import persist from './persist';
|
|
2
|
+
import detectNetwork from './detectNetwork';
|
|
3
|
+
import effect from './effect';
|
|
4
|
+
import retry from './retry';
|
|
5
|
+
import discard from './discard';
|
|
6
|
+
import defaultCommit from './defaultCommit';
|
|
7
|
+
import defaultRollback from './defaultRollback';
|
|
8
|
+
import persistAutoRehydrate from './persistAutoRehydrate';
|
|
9
|
+
import offlineStateLens from './offlineStateLens';
|
|
10
|
+
import queue from './queue';
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
rehydrate: true, // backward compatibility, TODO remove in the next breaking change version
|
|
14
|
+
persist,
|
|
15
|
+
detectNetwork,
|
|
16
|
+
effect,
|
|
17
|
+
retry,
|
|
18
|
+
discard,
|
|
19
|
+
defaultCommit,
|
|
20
|
+
defaultRollback,
|
|
21
|
+
persistAutoRehydrate,
|
|
22
|
+
offlineStateLens,
|
|
23
|
+
queue,
|
|
24
|
+
returnPromises: false
|
|
25
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
// $FlowIgnore
|
|
3
|
+
import { persistStore } from 'redux-persist';
|
|
4
|
+
// $FlowIgnore
|
|
5
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
6
|
+
|
|
7
|
+
export default (store: any, options: {}, callback: any) =>
|
|
8
|
+
// $FlowFixMe
|
|
9
|
+
persistStore(store, callback);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
|
|
3
|
+
function enqueue(array, item, context) {
|
|
4
|
+
return [...array, item];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function dequeue(array, item, context) {
|
|
8
|
+
const [, ...rest] = array;
|
|
9
|
+
return rest;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function peek(array, item, context) {
|
|
13
|
+
return array[0];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
enqueue,
|
|
18
|
+
dequeue,
|
|
19
|
+
peek
|
|
20
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type { OfflineAction } from '../types';
|
|
3
|
+
|
|
4
|
+
const decaySchedule = [
|
|
5
|
+
1000, // After 1 seconds
|
|
6
|
+
1000 * 5, // After 5 seconds
|
|
7
|
+
1000 * 15, // After 15 seconds
|
|
8
|
+
1000 * 30, // After 30 seconds
|
|
9
|
+
1000 * 60, // After 1 minute
|
|
10
|
+
1000 * 60 * 3, // After 3 minutes
|
|
11
|
+
1000 * 60 * 5, // After 5 minutes
|
|
12
|
+
1000 * 60 * 10, // After 10 minutes
|
|
13
|
+
1000 * 60 * 30, // After 30 minutes
|
|
14
|
+
1000 * 60 * 60 // After 1 hour
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export default (action: OfflineAction, retries: number): ?number =>
|
|
18
|
+
decaySchedule[retries] || null;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
/* global $Shape */
|
|
3
|
+
import { applyMiddleware, compose } from 'redux';
|
|
4
|
+
import type { Config } from './types';
|
|
5
|
+
import { createOfflineMiddleware } from './middleware';
|
|
6
|
+
import { enhanceReducer } from './updater';
|
|
7
|
+
import { applyDefaults } from './config';
|
|
8
|
+
import { networkStatusChanged } from './actions';
|
|
9
|
+
import offlineActionTracker from './offlineActionTracker';
|
|
10
|
+
|
|
11
|
+
// @TODO: Take createStore as config?
|
|
12
|
+
const warnIfNotReduxAction = (config: $Shape<Config>, key: string) => {
|
|
13
|
+
const maybeAction = config[key];
|
|
14
|
+
|
|
15
|
+
const isNotReduxAction =
|
|
16
|
+
maybeAction === null ||
|
|
17
|
+
typeof maybeAction !== 'object' ||
|
|
18
|
+
typeof maybeAction.type !== 'string' ||
|
|
19
|
+
maybeAction.type === '';
|
|
20
|
+
|
|
21
|
+
if (isNotReduxAction && console.warn) {
|
|
22
|
+
const msg =
|
|
23
|
+
`${key} must be a proper redux action, ` +
|
|
24
|
+
`i.e. it must be an object and have a non-empty string type. ` +
|
|
25
|
+
`Instead you provided: ${JSON.stringify(maybeAction, null, 2)}`;
|
|
26
|
+
console.warn(msg);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const offline = (userConfig: $Shape<Config> = {}) => (
|
|
31
|
+
createStore: any
|
|
32
|
+
) => (reducer: any, preloadedState: any, enhancer: any = x => x) => {
|
|
33
|
+
const config = applyDefaults(userConfig);
|
|
34
|
+
|
|
35
|
+
warnIfNotReduxAction(config, 'defaultCommit');
|
|
36
|
+
warnIfNotReduxAction(config, 'defaultRollback');
|
|
37
|
+
|
|
38
|
+
// toggle experimental returned promises
|
|
39
|
+
config.offlineActionTracker = config.returnPromises
|
|
40
|
+
? offlineActionTracker.withPromises
|
|
41
|
+
: offlineActionTracker.withoutPromises;
|
|
42
|
+
delete config.returnPromises;
|
|
43
|
+
|
|
44
|
+
// wraps userland reducer with a top-level
|
|
45
|
+
// reducer that handles offline state updating
|
|
46
|
+
const offlineReducer = enhanceReducer(reducer, config);
|
|
47
|
+
|
|
48
|
+
// $FlowFixMe
|
|
49
|
+
const offlineMiddleware = applyMiddleware(createOfflineMiddleware(config));
|
|
50
|
+
|
|
51
|
+
// create autoRehydrate enhancer if required
|
|
52
|
+
const offlineEnhancer =
|
|
53
|
+
config.persist && config.rehydrate && config.persistAutoRehydrate
|
|
54
|
+
? compose(offlineMiddleware, config.persistAutoRehydrate())
|
|
55
|
+
: offlineMiddleware;
|
|
56
|
+
|
|
57
|
+
// create store
|
|
58
|
+
const store = offlineEnhancer(createStore)(
|
|
59
|
+
offlineReducer,
|
|
60
|
+
preloadedState,
|
|
61
|
+
enhancer
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const baseReplaceReducer = store.replaceReducer.bind(store);
|
|
65
|
+
// $FlowFixMe
|
|
66
|
+
store.replaceReducer = function replaceReducer(nextReducer) {
|
|
67
|
+
return baseReplaceReducer(enhanceReducer(nextReducer, config));
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// launch store persistor
|
|
71
|
+
if (config.persist) {
|
|
72
|
+
config.persist(store, config.persistOptions, config.persistCallback);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// launch network detector
|
|
76
|
+
if (config.detectNetwork) {
|
|
77
|
+
config.detectNetwork(online => {
|
|
78
|
+
store.dispatch(networkStatusChanged(online));
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return store;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const createOffline = (userConfig: $Shape<Config> = {}) => {
|
|
86
|
+
const config = applyDefaults(userConfig);
|
|
87
|
+
|
|
88
|
+
// toggle experimental returned promises
|
|
89
|
+
config.offlineActionTracker = config.returnPromises
|
|
90
|
+
? offlineActionTracker.withPromises
|
|
91
|
+
: offlineActionTracker.withoutPromises;
|
|
92
|
+
delete config.returnPromises;
|
|
93
|
+
|
|
94
|
+
warnIfNotReduxAction(config, 'defaultCommit');
|
|
95
|
+
warnIfNotReduxAction(config, 'defaultRollback');
|
|
96
|
+
|
|
97
|
+
const enhanceStore = (next: any) => (
|
|
98
|
+
reducer: any,
|
|
99
|
+
preloadedState: any,
|
|
100
|
+
enhancer: any
|
|
101
|
+
) => {
|
|
102
|
+
// create autoRehydrate enhancer if required
|
|
103
|
+
const createStore =
|
|
104
|
+
config.persist && config.rehydrate && config.persistAutoRehydrate
|
|
105
|
+
? config.persistAutoRehydrate()(next)
|
|
106
|
+
: next;
|
|
107
|
+
|
|
108
|
+
// create store
|
|
109
|
+
const store = createStore(reducer, preloadedState, enhancer);
|
|
110
|
+
|
|
111
|
+
const baseReplaceReducer = store.replaceReducer.bind(store);
|
|
112
|
+
store.replaceReducer = function replaceReducer(nextReducer) {
|
|
113
|
+
return baseReplaceReducer(enhanceReducer(nextReducer, config));
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// launch store persistor
|
|
117
|
+
if (config.persist) {
|
|
118
|
+
config.persist(store, config.persistOptions, config.persistCallback);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// launch network detector
|
|
122
|
+
if (config.detectNetwork) {
|
|
123
|
+
config.detectNetwork(online => {
|
|
124
|
+
store.dispatch(networkStatusChanged(online));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return store;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
middleware: createOfflineMiddleware(config),
|
|
133
|
+
enhanceReducer(reducer: any) {
|
|
134
|
+
return enhanceReducer(reducer, config);
|
|
135
|
+
},
|
|
136
|
+
enhanceStore
|
|
137
|
+
};
|
|
138
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import type { AppState, Config } from './types';
|
|
4
|
+
import { OFFLINE_SEND, OFFLINE_SCHEDULE_RETRY } from './constants';
|
|
5
|
+
import { completeRetry } from './actions';
|
|
6
|
+
import send from './send';
|
|
7
|
+
|
|
8
|
+
const after = (timeout = 0) =>
|
|
9
|
+
new Promise(resolve => setTimeout(resolve, timeout));
|
|
10
|
+
|
|
11
|
+
export const createOfflineMiddleware = (config: Config) => (store: any) => (
|
|
12
|
+
next: any
|
|
13
|
+
) => (action: any) => {
|
|
14
|
+
// allow other middleware to do their things
|
|
15
|
+
const result = next(action);
|
|
16
|
+
let promise;
|
|
17
|
+
|
|
18
|
+
// find any actions to send, if any
|
|
19
|
+
const state: AppState = store.getState();
|
|
20
|
+
const offline = config.offlineStateLens(state).get;
|
|
21
|
+
const context = { offline };
|
|
22
|
+
const offlineAction = config.queue.peek(offline.outbox, action, context);
|
|
23
|
+
|
|
24
|
+
// create promise to return on enqueue offline action
|
|
25
|
+
if (action.meta && action.meta.offline) {
|
|
26
|
+
const { registerAction } = config.offlineActionTracker;
|
|
27
|
+
promise = registerAction(offline.lastTransaction);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// if there are any actions in the queue that we are not
|
|
31
|
+
// yet processing, send those actions
|
|
32
|
+
if (
|
|
33
|
+
offlineAction &&
|
|
34
|
+
!offline.busy &&
|
|
35
|
+
!offline.retryScheduled &&
|
|
36
|
+
offline.online
|
|
37
|
+
) {
|
|
38
|
+
send(offlineAction, store.dispatch, config, offline.retryCount);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (action.type === OFFLINE_SCHEDULE_RETRY) {
|
|
42
|
+
after(action.payload.delay).then(() => {
|
|
43
|
+
store.dispatch(completeRetry(offlineAction));
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (action.type === OFFLINE_SEND && offlineAction && !offline.busy) {
|
|
48
|
+
send(offlineAction, store.dispatch, config, offline.retryCount);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return promise || result;
|
|
52
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const subscriptions = {};
|
|
2
|
+
|
|
3
|
+
function registerAction(transaction) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
subscriptions[transaction] = { resolve, reject };
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function resolveAction(transaction, value) {
|
|
10
|
+
const subscription = subscriptions[transaction];
|
|
11
|
+
if (subscription) {
|
|
12
|
+
subscription.resolve(value);
|
|
13
|
+
delete subscriptions[transaction];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function rejectAction(transaction, error) {
|
|
18
|
+
const subscription = subscriptions[transaction];
|
|
19
|
+
if (subscription) {
|
|
20
|
+
subscription.reject(error);
|
|
21
|
+
delete subscriptions[transaction];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const withPromises = {
|
|
26
|
+
registerAction,
|
|
27
|
+
resolveAction,
|
|
28
|
+
rejectAction
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const withoutPromises = {
|
|
32
|
+
registerAction() {},
|
|
33
|
+
resolveAction() {},
|
|
34
|
+
rejectAction() {}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default {
|
|
38
|
+
withPromises,
|
|
39
|
+
withoutPromises
|
|
40
|
+
};
|