@dhis2/app-service-offline 3.11.2 → 3.12.0-alpha.1

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 (60) hide show
  1. package/build/cjs/__tests__/integration.test.js +51 -82
  2. package/build/cjs/index.js +0 -7
  3. package/build/cjs/lib/__tests__/cacheable-section-state.test.js +7 -14
  4. package/build/cjs/lib/__tests__/clear-sensitive-caches.test.js +17 -20
  5. package/build/cjs/lib/__tests__/network-status.test.js +135 -148
  6. package/build/cjs/lib/__tests__/offline-provider.test.js +12 -22
  7. package/build/cjs/lib/__tests__/use-cacheable-section.test.js +87 -98
  8. package/build/cjs/lib/__tests__/use-online-status-message.test.js +7 -14
  9. package/build/cjs/lib/cacheable-section-state.js +27 -38
  10. package/build/cjs/lib/cacheable-section.js +26 -27
  11. package/build/cjs/lib/clear-sensitive-caches.js +14 -24
  12. package/build/cjs/lib/dhis2-connection-status/dev-debug-log.js +1 -3
  13. package/build/cjs/lib/dhis2-connection-status/dhis2-connection-status.js +27 -58
  14. package/build/cjs/lib/dhis2-connection-status/dhis2-connection-status.test.js +287 -230
  15. package/build/cjs/lib/dhis2-connection-status/index.js +0 -1
  16. package/build/cjs/lib/dhis2-connection-status/is-ping-available.js +0 -6
  17. package/build/cjs/lib/dhis2-connection-status/is-ping-available.test.js +0 -1
  18. package/build/cjs/lib/dhis2-connection-status/smart-interval.js +35 -49
  19. package/build/cjs/lib/dhis2-connection-status/use-ping-query.js +4 -5
  20. package/build/cjs/lib/global-state-service.js +9 -27
  21. package/build/cjs/lib/network-status.js +10 -13
  22. package/build/cjs/lib/offline-interface.js +3 -14
  23. package/build/cjs/lib/offline-provider.js +1 -12
  24. package/build/cjs/lib/online-status-message.js +5 -17
  25. package/build/cjs/setupRTL.js +1 -1
  26. package/build/cjs/utils/__tests__/render-counter.test.js +3 -12
  27. package/build/cjs/utils/render-counter.js +2 -10
  28. package/build/cjs/utils/test-mocks.js +13 -18
  29. package/build/es/__tests__/integration.test.js +51 -74
  30. package/build/es/index.js +2 -2
  31. package/build/es/lib/__tests__/cacheable-section-state.test.js +2 -4
  32. package/build/es/lib/__tests__/clear-sensitive-caches.test.js +19 -16
  33. package/build/es/lib/__tests__/network-status.test.js +105 -114
  34. package/build/es/lib/__tests__/offline-provider.test.js +13 -15
  35. package/build/es/lib/__tests__/use-cacheable-section.test.js +69 -73
  36. package/build/es/lib/__tests__/use-online-status-message.test.js +2 -3
  37. package/build/es/lib/cacheable-section-state.js +25 -26
  38. package/build/es/lib/cacheable-section.js +23 -15
  39. package/build/es/lib/clear-sensitive-caches.js +13 -21
  40. package/build/es/lib/dhis2-connection-status/dev-debug-log.js +1 -3
  41. package/build/es/lib/dhis2-connection-status/dhis2-connection-status.js +26 -37
  42. package/build/es/lib/dhis2-connection-status/dhis2-connection-status.test.js +223 -159
  43. package/build/es/lib/dhis2-connection-status/is-ping-available.js +0 -5
  44. package/build/es/lib/dhis2-connection-status/smart-interval.js +34 -42
  45. package/build/es/lib/dhis2-connection-status/use-ping-query.js +6 -3
  46. package/build/es/lib/global-state-service.js +6 -12
  47. package/build/es/lib/network-status.js +10 -9
  48. package/build/es/lib/offline-interface.js +0 -3
  49. package/build/es/lib/offline-provider.js +0 -3
  50. package/build/es/lib/online-status-message.js +3 -2
  51. package/build/es/setupRTL.js +1 -1
  52. package/build/es/utils/__tests__/render-counter.test.js +2 -4
  53. package/build/es/utils/render-counter.js +1 -3
  54. package/build/es/utils/test-mocks.js +8 -9
  55. package/build/types/lib/cacheable-section.d.ts +1 -1
  56. package/build/types/lib/dhis2-connection-status/dhis2-connection-status.d.ts +1 -1
  57. package/build/types/lib/network-status.d.ts +1 -1
  58. package/build/types/lib/online-status-message.d.ts +1 -1
  59. package/build/types/types.d.ts +1 -1
  60. package/package.json +4 -4
@@ -1,10 +1,10 @@
1
1
  // IndexedDB names; should be the same as in @dhis2/pwa
2
2
  export const SECTIONS_DB = 'sections-db';
3
- export const SECTIONS_STORE = 'sections-store'; // Non-sensitive caches that can be kept:
3
+ export const SECTIONS_STORE = 'sections-store';
4
4
 
5
+ // Non-sensitive caches that can be kept:
5
6
  const KEEPABLE_CACHES = [/^workbox-precache/ // precached static assets
6
7
  ];
7
-
8
8
  /*
9
9
  * Clears the 'sections-db' IndexedDB if it exists. Designed to avoid opening
10
10
  * a new DB if it doesn't exist yet. Firefox can't check if 'sections-db'
@@ -19,10 +19,8 @@ const clearDB = async dbName => {
19
19
  // and offline interface will handle discrepancies in PWA apps.
20
20
  return;
21
21
  }
22
-
23
22
  const dbs = await window.indexedDB.databases();
24
-
25
- if (!dbs.some((_ref) => {
23
+ if (!dbs.some(_ref => {
26
24
  let {
27
25
  name
28
26
  } = _ref;
@@ -31,57 +29,51 @@ const clearDB = async dbName => {
31
29
  // Sections-db is not created; nothing to do here
32
30
  return;
33
31
  }
34
-
35
32
  return new Promise((resolve, reject) => {
36
33
  // IndexedDB fun:
37
34
  const openDBRequest = indexedDB.open(dbName);
38
-
39
35
  openDBRequest.onsuccess = e => {
40
36
  const db = e.target.result;
41
- const tx = db.transaction(SECTIONS_STORE, 'readwrite'); // When the transaction completes is when the operation is done:
42
-
37
+ const tx = db.transaction(SECTIONS_STORE, 'readwrite');
38
+ // When the transaction completes is when the operation is done:
43
39
  tx.oncomplete = () => resolve();
44
-
45
40
  tx.onerror = e => reject(e.target.error);
46
-
47
41
  const os = tx.objectStore(SECTIONS_STORE);
48
42
  const clearReq = os.clear();
49
-
50
43
  clearReq.onerror = e => reject(e.target.error);
51
44
  };
52
-
53
45
  openDBRequest.onerror = e => {
54
46
  reject(e.target.error);
55
47
  };
56
48
  });
57
49
  };
50
+
58
51
  /**
59
52
  * Used to clear caches and 'sections-db' IndexedDB when a user logs out or a
60
53
  * different user logs in to prevent someone from accessing a different user's
61
54
  * caches. Should be able to be used in a non-PWA app.
62
55
  */
63
-
64
-
65
56
  export async function clearSensitiveCaches() {
66
57
  let dbName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : SECTIONS_DB;
67
58
  console.debug('Clearing sensitive caches');
68
- let cacheKeys; // caches.keys can fail in insecure contexts, see:
69
- // https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage
59
+ let cacheKeys;
70
60
 
61
+ // caches.keys can fail in insecure contexts, see:
62
+ // https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage
71
63
  try {
72
64
  cacheKeys = await caches.keys();
73
65
  } catch (e) {
74
66
  // Return false since no caches have been cleared
75
67
  return false;
76
68
  }
77
-
78
- return Promise.all([// (Resolves to 'false' because this can't detect if anything was deleted):
79
- clearDB(dbName).then(() => false), // Remove caches if not in keepable list
69
+ return Promise.all([
70
+ // (Resolves to 'false' because this can't detect if anything was deleted):
71
+ clearDB(dbName).then(() => false),
72
+ // Remove caches if not in keepable list
80
73
  ...cacheKeys.map(key => {
81
74
  if (!KEEPABLE_CACHES.some(pattern => pattern.test(key))) {
82
75
  return caches.delete(key);
83
76
  }
84
-
85
77
  return false;
86
78
  })]).then(responses => {
87
79
  // Return true if any caches have been cleared
@@ -1,8 +1,8 @@
1
1
  const shouldLog = localStorage.getItem('dhis2.debugConnectionStatus');
2
-
3
2
  if (shouldLog) {
4
3
  console.log('Logging for dhis2ConnectionStatus is enabled. Remove the `dhis2.debugConnectionStatus` item in localStorage to disable logging.');
5
4
  }
5
+
6
6
  /**
7
7
  * This can be used to log info if the `dhis2.debugConnectionStatus` value
8
8
  * in localStorage is set to a truthy value during development.
@@ -11,8 +11,6 @@ if (shouldLog) {
11
11
  * The behavior of the connection status can be quite hard to inspect without
12
12
  * logs, but the logs are quite chatty and should be omitted normally.
13
13
  */
14
-
15
-
16
14
  export function devDebugLog() {
17
15
  if (shouldLog) {
18
16
  console.log(...arguments);
@@ -6,32 +6,30 @@ import { useOfflineInterface } from '../offline-interface';
6
6
  import { devDebugLog } from './dev-debug-log';
7
7
  import { isPingAvailable } from './is-ping-available';
8
8
  import createSmartInterval from './smart-interval';
9
- import { usePingQuery } from './use-ping-query'; // Utils for saving 'last connected' datetime in local storage
9
+ import { usePingQuery } from './use-ping-query';
10
10
 
11
+ // Utils for saving 'last connected' datetime in local storage
11
12
  const lastConnectedKey = 'dhis2.lastConnected';
12
13
  export const getLastConnectedKey = appName => appName ? `${lastConnectedKey}.${appName}` : lastConnectedKey;
13
-
14
14
  const updateLastConnected = appName => {
15
15
  // use Date.now() because it's easier to mock for easier unit testing
16
16
  const now = new Date(Date.now());
17
17
  localStorage.setItem(getLastConnectedKey(appName), now.toUTCString());
18
18
  return now;
19
19
  };
20
-
21
20
  const getLastConnected = appName => {
22
21
  const lastConnected = localStorage.getItem(getLastConnectedKey(appName));
23
22
  return lastConnected ? new Date(lastConnected) : null;
24
23
  };
25
-
26
24
  const clearLastConnected = appName => {
27
25
  localStorage.removeItem(getLastConnectedKey(appName));
28
26
  };
29
-
30
27
  const Dhis2ConnectionStatusContext = /*#__PURE__*/React.createContext({
31
28
  isConnected: true,
32
29
  isDisconnected: false,
33
30
  lastConnected: null
34
31
  });
32
+
35
33
  /**
36
34
  * Provides a boolean indicating client's connection to the DHIS2 server,
37
35
  * which is different from connection to the internet.
@@ -41,8 +39,7 @@ const Dhis2ConnectionStatusContext = /*#__PURE__*/React.createContext({
41
39
  * and then will initiate periodic pings if there are no incidental requests in
42
40
  * order to check the connection consistently
43
41
  */
44
-
45
- export const Dhis2ConnectionStatusProvider = (_ref) => {
42
+ export const Dhis2ConnectionStatusProvider = _ref => {
46
43
  let {
47
44
  children
48
45
  } = _ref;
@@ -50,21 +47,21 @@ export const Dhis2ConnectionStatusProvider = (_ref) => {
50
47
  const {
51
48
  appName,
52
49
  serverVersion
53
- } = useConfig(); // The offline interface persists the latest update from the SW so that
50
+ } = useConfig();
51
+ // The offline interface persists the latest update from the SW so that
54
52
  // this hook can initialize to an accurate value. The App Adapter in the
55
53
  // platform waits for this value to be populated before rendering the
56
54
  // the App Runtime provider (including this), but if that is not done,
57
55
  // `latestIsConnected` may be `null` depending on the outcome of race
58
56
  // conditions between the SW and the React component tree.
59
-
60
57
  const [isConnected, setIsConnected] = useState(offlineInterface.latestIsConnected);
61
58
  const ping = usePingQuery();
62
59
  const smartIntervalRef = useRef(null);
60
+
63
61
  /**
64
62
  * Update state, reset ping backoff if changed, and update
65
63
  * the lastConnected value in localStorage
66
64
  */
67
-
68
65
  const updateConnectedState = useCallback(newIsConnected => {
69
66
  // use 'set' with a function as param to get latest isConnected
70
67
  // without needing it as a dependency for useCallback
@@ -73,13 +70,10 @@ export const Dhis2ConnectionStatusProvider = (_ref) => {
73
70
  prevIsConnected,
74
71
  newIsConnected
75
72
  });
76
-
77
73
  if (newIsConnected !== prevIsConnected) {
78
74
  var _smartIntervalRef$cur;
79
-
80
75
  // if value changed, reset ping interval to initial delay
81
76
  (_smartIntervalRef$cur = smartIntervalRef.current) === null || _smartIntervalRef$cur === void 0 ? void 0 : _smartIntervalRef$cur.reset();
82
-
83
77
  if (newIsConnected) {
84
78
  // Need to clear this here so it doesn't affect another
85
79
  // session that starts while offline
@@ -88,15 +82,15 @@ export const Dhis2ConnectionStatusProvider = (_ref) => {
88
82
  updateLastConnected(appName);
89
83
  }
90
84
  }
91
-
92
85
  return newIsConnected;
93
86
  });
94
- }, [appName]); // Note that the SW is configured to not cache ping requests and won't
87
+ }, [appName]);
88
+
89
+ // Note that the SW is configured to not cache ping requests and won't
95
90
  // trigger `handleChange` below to avoid redundant signals. This also
96
91
  // helps to detect the connectivity status when the SW is not available
97
92
  // for some reason (maybe private browsing, first installation, or
98
93
  // insecure browser context)
99
-
100
94
  const pingAndHandleStatus = useCallback(() => {
101
95
  return ping().then(() => {
102
96
  // Ping is successful; set 'connected'
@@ -106,16 +100,16 @@ export const Dhis2ConnectionStatusProvider = (_ref) => {
106
100
  updateConnectedState(false);
107
101
  });
108
102
  }, [ping, updateConnectedState]);
109
- /** Called when SW reports updates from incidental network traffic */
110
103
 
111
- const onUpdate = useCallback((_ref2) => {
104
+ /** Called when SW reports updates from incidental network traffic */
105
+ const onUpdate = useCallback(_ref2 => {
112
106
  var _smartIntervalRef$cur2;
113
-
114
107
  let {
115
108
  isConnected: newIsConnected
116
109
  } = _ref2;
117
- devDebugLog('[D2CS] handling update from sw'); // Snooze ping timer to reduce pings since we know state from SW
110
+ devDebugLog('[D2CS] handling update from sw');
118
111
 
112
+ // Snooze ping timer to reduce pings since we know state from SW
119
113
  (_smartIntervalRef$cur2 = smartIntervalRef.current) === null || _smartIntervalRef$cur2 === void 0 ? void 0 : _smartIntervalRef$cur2.snooze();
120
114
  updateConnectedState(newIsConnected);
121
115
  }, [updateConnectedState]);
@@ -124,26 +118,22 @@ export const Dhis2ConnectionStatusProvider = (_ref) => {
124
118
  // pinging with the smart interval. Just use the service worker
125
119
  if (!serverVersion || !isPingAvailable(serverVersion)) {
126
120
  return;
127
- } // Only create the smart interval once
128
-
121
+ }
129
122
 
123
+ // Only create the smart interval once
130
124
  const smartInterval = createSmartInterval({
131
125
  // don't ping if window isn't focused or visible
132
126
  initialPauseValue: !document.hasFocus() || document.visibilityState !== 'visible',
133
127
  callback: pingAndHandleStatus
134
128
  });
135
129
  smartIntervalRef.current = smartInterval;
136
-
137
130
  const handleBlur = () => smartInterval.pause();
138
-
139
- const handleFocus = () => smartInterval.resume(); // Pinging when going offline should be low/no-cost in both online and
131
+ const handleFocus = () => smartInterval.resume();
132
+ // Pinging when going offline should be low/no-cost in both online and
140
133
  // local servers
141
-
142
-
143
- const handleOffline = () => smartInterval.invokeCallbackImmediately(); // Pinging when going online has a cost but improves responsiveness of
134
+ const handleOffline = () => smartInterval.invokeCallbackImmediately();
135
+ // Pinging when going online has a cost but improves responsiveness of
144
136
  // the connection status -- only do it once every 15 seconds at most
145
-
146
-
147
137
  const handleOnline = throttle(() => smartInterval.invokeCallbackImmediately(), 15000);
148
138
  window.addEventListener('blur', handleBlur);
149
139
  window.addEventListener('focus', handleFocus);
@@ -153,8 +143,9 @@ export const Dhis2ConnectionStatusProvider = (_ref) => {
153
143
  window.removeEventListener('blur', handleBlur);
154
144
  window.removeEventListener('focus', handleFocus);
155
145
  window.removeEventListener('offline', handleOffline);
156
- window.removeEventListener('online', handleOnline); // clean up smart interval and throttled function
146
+ window.removeEventListener('online', handleOnline);
157
147
 
148
+ // clean up smart interval and throttled function
158
149
  smartInterval.clear();
159
150
  handleOnline.cancel();
160
151
  };
@@ -162,22 +153,21 @@ export const Dhis2ConnectionStatusProvider = (_ref) => {
162
153
  useEffect(() => {
163
154
  if (!offlineInterface.subscribeToDhis2ConnectionStatus) {
164
155
  var _smartIntervalRef$cur3;
165
-
166
156
  // Missing this functionality from the offline interface --
167
157
  // use a ping on startup to get the status
168
158
  (_smartIntervalRef$cur3 = smartIntervalRef.current) === null || _smartIntervalRef$cur3 === void 0 ? void 0 : _smartIntervalRef$cur3.invokeCallbackImmediately();
169
159
  console.warn('Please upgrade to @dhis2/cli-app-scripts@>10.3.8 for full connection status features');
170
160
  return;
171
161
  }
172
-
173
162
  const unsubscribe = offlineInterface.subscribeToDhis2ConnectionStatus({
174
163
  onUpdate
175
164
  });
176
165
  return () => {
177
166
  unsubscribe();
178
167
  };
179
- }, [offlineInterface, onUpdate]); // Memoize this value to prevent unnecessary rerenders of context provider
168
+ }, [offlineInterface, onUpdate]);
180
169
 
170
+ // Memoize this value to prevent unnecessary rerenders of context provider
181
171
  const contextValue = useMemo(() => {
182
172
  // in the unlikely circumstance that offlineInterface.latestIsConnected
183
173
  // is `null` or `undefined` when this initializes, fail safe by defaulting to
@@ -186,7 +176,8 @@ export const Dhis2ConnectionStatusProvider = (_ref) => {
186
176
  return {
187
177
  isConnected: validatedIsConnected,
188
178
  isDisconnected: !validatedIsConnected,
189
- lastConnected: validatedIsConnected ? null : // Only evaluate if disconnected, since local storage
179
+ lastConnected: validatedIsConnected ? null :
180
+ // Only evaluate if disconnected, since local storage
190
181
  // is synchronous and disk-based.
191
182
  // If lastConnected is not set in localStorage though, set it.
192
183
  // (relevant on startup)
@@ -202,10 +193,8 @@ Dhis2ConnectionStatusProvider.propTypes = {
202
193
  };
203
194
  export const useDhis2ConnectionStatus = () => {
204
195
  const context = useContext(Dhis2ConnectionStatusContext);
205
-
206
196
  if (!context) {
207
197
  throw new Error('useDhis2ConnectionStatus must be used within a Dhis2ConnectionStatus provider');
208
198
  }
209
-
210
199
  return context;
211
200
  };