@dhis2/app-service-offline
Advanced tools
Comparing version 2.10.0-pwa.1 to 2.10.0-pwa.2
@@ -55,3 +55,4 @@ "use strict"; | ||
const TestSection = ({ | ||
id | ||
id, | ||
children | ||
}) => /*#__PURE__*/_react2.default.createElement(_cacheableSection.CacheableSection, { | ||
@@ -65,3 +66,3 @@ id: id, | ||
countsObj: renderCounts | ||
})); | ||
}), children); | ||
@@ -95,3 +96,4 @@ const TestSingleSection = props => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
jest.clearAllMocks() // This syntax appeases typescript: | ||
; | ||
console.error.mockRestore(); | ||
@@ -286,2 +288,54 @@ (0, _renderCounter.resetRenderCounts)(renderCounts); | ||
}); | ||
}); | ||
describe('useCacheableSection can be used inside a child of CacheableSection', () => { | ||
const ChildTest = props => { | ||
// Props are spread so they can be overwritten | ||
return /*#__PURE__*/_react2.default.createElement(_offlineProvider.OfflineProvider, _extends({ | ||
offlineInterface: _testMocks.mockOfflineInterface | ||
}, props), /*#__PURE__*/_react2.default.createElement(TestSection, _extends({ | ||
id: '1' | ||
}, props), /*#__PURE__*/_react2.default.createElement(TestControls, _extends({ | ||
id: '1' | ||
}, props)))); | ||
}; | ||
it('handles a successful recording', async done => { | ||
const { | ||
getByTestId, | ||
queryByTestId | ||
} = _react.screen; | ||
const onStarted = () => { | ||
expect(getByTestId(/recording-state/)).toHaveTextContent('recording'); | ||
expect(getByTestId(/loading-mask/)).toBeInTheDocument(); | ||
expect(getByTestId(/section-rc/)).toBeInTheDocument(); | ||
}; | ||
const onCompleted = () => { | ||
expect(getByTestId(/recording-state/)).toHaveTextContent('default'); | ||
expect(queryByTestId(/loading-mask/)).not.toBeInTheDocument(); | ||
done(); | ||
}; | ||
const recordingOptions = { | ||
onStarted, | ||
onCompleted | ||
}; | ||
const makeRecordingHandler = startRecording => { | ||
return () => startRecording(recordingOptions); | ||
}; | ||
(0, _react.render)( /*#__PURE__*/_react2.default.createElement(ChildTest, { | ||
makeRecordingHandler: makeRecordingHandler | ||
})); | ||
await (0, _react.act)(async () => { | ||
_react.fireEvent.click(getByTestId(/start-recording/)); | ||
}); // At this stage, should be pending | ||
// - In this test case, 'controls' should not be rendered | ||
expect(queryByTestId(/recording-state/)).not.toBeInTheDocument(); | ||
expect(queryByTestId(/section-rc/)).not.toBeInTheDocument(); | ||
expect.assertions(7); | ||
}); | ||
}); |
@@ -31,3 +31,4 @@ "use strict"; | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
jest.clearAllMocks() // syntax needed to appease typescript | ||
; | ||
console.error.mockRestore(); | ||
@@ -82,3 +83,3 @@ }); | ||
await (0, _react.waitFor)(() => getByTestId('sections').textContent !== '{}'); | ||
const textContent = JSON.parse(getByTestId('sections').textContent); | ||
const textContent = JSON.parse(getByTestId('sections').textContent || ''); | ||
expect(textContent).toEqual({ | ||
@@ -97,2 +98,3 @@ 1: { | ||
return /*#__PURE__*/_react2.default.createElement(_cacheableSection.CacheableSection, { | ||
loadingMask: /*#__PURE__*/_react2.default.createElement("div", null), | ||
id: 'id' | ||
@@ -99,0 +101,0 @@ }, /*#__PURE__*/_react2.default.createElement("div", { |
@@ -15,3 +15,3 @@ "use strict"; | ||
result | ||
} = (0, _reactHooks.renderHook)((...args) => (0, _onlineStatus.useOnlineStatus)(...args)); | ||
} = (0, _reactHooks.renderHook)(() => (0, _onlineStatus.useOnlineStatus)()); | ||
expect(result.current.online).toBe(true); | ||
@@ -24,3 +24,3 @@ expect(result.current.offline).toBe(false); | ||
result | ||
} = (0, _reactHooks.renderHook)((...args) => (0, _onlineStatus.useOnlineStatus)(...args)); | ||
} = (0, _reactHooks.renderHook)(() => (0, _onlineStatus.useOnlineStatus)()); | ||
expect(result.current.online).toBe(false); | ||
@@ -39,8 +39,6 @@ expect(result.current.offline).toBe(true); | ||
waitForNextUpdate | ||
} = (0, _reactHooks.renderHook)((...args) => (0, _onlineStatus.useOnlineStatus)(...args)); | ||
} = (0, _reactHooks.renderHook)(() => (0, _onlineStatus.useOnlineStatus)()); | ||
(0, _reactHooks.act)(() => { | ||
// Trigger callback captured by addEventListener mock | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.offline(new Event('offline')); | ||
}); // Wait for debounce | ||
@@ -61,7 +59,5 @@ | ||
waitForNextUpdate | ||
} = (0, _reactHooks.renderHook)((...args) => (0, _onlineStatus.useOnlineStatus)(...args)); | ||
} = (0, _reactHooks.renderHook)(() => (0, _onlineStatus.useOnlineStatus)()); | ||
(0, _reactHooks.act)(() => { | ||
events.online({ | ||
type: 'online' | ||
}); | ||
events.online(new Event('online')); | ||
}); // Wait for debounce | ||
@@ -85,14 +81,8 @@ | ||
waitForNextUpdate | ||
} = (0, _reactHooks.renderHook)((...args) => (0, _onlineStatus.useOnlineStatus)(...args)); | ||
} = (0, _reactHooks.renderHook)(() => (0, _onlineStatus.useOnlineStatus)()); | ||
await (0, _reactHooks.act)(async () => { | ||
// Multiple events in succession | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.online({ | ||
type: 'online' | ||
}); | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.offline(new Event('offline')); | ||
events.online(new Event('online')); | ||
events.offline(new Event('offline')); | ||
}); // Immediately, nothing should happen | ||
@@ -122,11 +112,5 @@ | ||
// Multiple events in succession | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.online({ | ||
type: 'online' | ||
}); | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.offline(new Event('offline')); | ||
events.online(new Event('online')); | ||
events.offline(new Event('offline')); | ||
}); // Immediately, nothing should happen | ||
@@ -133,0 +117,0 @@ |
@@ -30,3 +30,4 @@ "use strict"; | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
jest.clearAllMocks() // This syntax appeases typescript: | ||
; | ||
console.error.mockRestore(); | ||
@@ -33,0 +34,0 @@ }); |
@@ -22,5 +22,2 @@ "use strict"; | ||
// Functions in here use the global state service to manage cacheable section | ||
// state in a performant way | ||
/** | ||
@@ -87,7 +84,9 @@ * Helper that transforms an array of cached section objects from the IndexedDB | ||
_react.default.useEffect(() => { | ||
offlineInterface.getCachedSections().then(sections => { | ||
store.mutate(state => ({ ...state, | ||
cachedSections: getSectionsById(sections) | ||
})); | ||
}); | ||
if (offlineInterface) { | ||
offlineInterface.getCachedSections().then(sections => { | ||
store.mutate(state => ({ ...state, | ||
cachedSections: getSectionsById(sections) | ||
})); | ||
}); | ||
} | ||
}, [store, offlineInterface]); | ||
@@ -103,2 +102,3 @@ | ||
}; | ||
/** | ||
@@ -111,3 +111,2 @@ * Uses an optimized global state to manage 'recording state' values without | ||
*/ | ||
function useRecordingState(id) { | ||
@@ -152,2 +151,3 @@ const [recordingState] = (0, _globalStateService.useGlobalState)(state => state.recordingStates[id]); | ||
} | ||
/** | ||
@@ -158,4 +158,2 @@ * Uses global state to manage an object of cached sections' statuses | ||
*/ | ||
function useCachedSections() { | ||
@@ -189,2 +187,3 @@ const [cachedSections] = (0, _globalStateService.useGlobalState)(state => state.cachedSections); | ||
} | ||
/** | ||
@@ -197,4 +196,2 @@ * Uses global state to manage the cached status of just one section, which | ||
*/ | ||
function useCachedSection(id) { | ||
@@ -201,0 +198,0 @@ const [status] = (0, _globalStateService.useGlobalState)(state => state.cachedSections[id]); |
@@ -29,2 +29,3 @@ "use strict"; | ||
}; | ||
/** | ||
@@ -39,3 +40,2 @@ * Returns the main controls for a cacheable section and manages recording | ||
*/ | ||
function useCacheableSection(id) { | ||
@@ -55,6 +55,13 @@ const offlineInterface = (0, _offlineInterface.useOfflineInterface)(); | ||
(0, _react.useEffect)(() => { | ||
// On mount, add recording state for this ID to context | ||
setRecordingState(recordingStates.default); // On unnmount, remove recording state | ||
// On mount, add recording state for this ID to context if needed | ||
if (!recordingState) { | ||
setRecordingState(recordingStates.default); | ||
} // On unnmount, remove recording state if not recording | ||
return removeRecordingState; | ||
return () => { | ||
if (recordingState && recordingState !== recordingStates.recording && recordingState !== recordingStates.pending) { | ||
removeRecordingState(); | ||
} | ||
}; | ||
}, []); // eslint-disable-line react-hooks/exhaustive-deps | ||
@@ -74,13 +81,13 @@ | ||
recordingTimeoutDelay, | ||
onStarted: (...args) => { | ||
onRecordingStarted(...args); | ||
onStarted && onStarted(...args); | ||
onStarted: () => { | ||
onRecordingStarted(); | ||
onStarted && onStarted(); | ||
}, | ||
onCompleted: (...args) => { | ||
onRecordingCompleted(...args); | ||
onCompleted && onCompleted(...args); | ||
onCompleted: () => { | ||
onRecordingCompleted(); | ||
onCompleted && onCompleted(); | ||
}, | ||
onError: (...args) => { | ||
onRecordingError(...args); | ||
onError && onError(...args); | ||
onError: error => { | ||
onRecordingError(error); | ||
onError && onError(error); | ||
} | ||
@@ -114,2 +121,3 @@ }).then(() => setRecordingState(recordingStates.pending)); | ||
} | ||
/** | ||
@@ -126,4 +134,2 @@ * Used to wrap the relevant component to be recorded and saved offline. | ||
*/ | ||
function CacheableSection({ | ||
@@ -143,5 +149,8 @@ id, | ||
if (recordingState === recordingStates.error) return children; // Handling rendering with the following conditions prevents an unncessary | ||
if (recordingState === recordingStates.error) { | ||
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children); | ||
} // Handling rendering with the following conditions prevents an unncessary | ||
// rerender after successful recording | ||
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, recordingState === recordingStates.recording && loadingMask, recordingState !== recordingStates.pending && children); | ||
@@ -148,0 +157,0 @@ } |
@@ -6,3 +6,4 @@ "use strict"; | ||
}); | ||
exports.useGlobalStateMutation = exports.useGlobalState = exports.GlobalStateProvider = exports.createStore = void 0; | ||
exports.useGlobalStateMutation = useGlobalStateMutation; | ||
exports.useGlobalState = exports.GlobalStateProvider = exports.createStore = void 0; | ||
@@ -88,3 +89,3 @@ var _isEqual = _interopRequireDefault(require("lodash/isEqual")); | ||
const useGlobalStateMutation = mutationCreator => { | ||
function useGlobalStateMutation(mutationCreator) { | ||
const store = useGlobalStateStore(); | ||
@@ -94,4 +95,2 @@ return (0, _react.useCallback)((...args) => { | ||
}, [mutationCreator, store]); | ||
}; | ||
exports.useGlobalStateMutation = useGlobalStateMutation; | ||
} |
@@ -21,3 +21,12 @@ "use strict"; | ||
const OfflineContext = /*#__PURE__*/(0, _react.createContext)(); | ||
// This is to prevent 'offlineInterface could be null' type-checking errors | ||
const noopOfflineInterface = { | ||
pwaEnabled: false, | ||
init: () => () => null, | ||
startRecording: async () => undefined, | ||
getCachedSections: async () => [], | ||
removeSection: async () => false | ||
}; | ||
const OfflineInterfaceContext = /*#__PURE__*/(0, _react.createContext)(noopOfflineInterface); | ||
/** | ||
@@ -31,3 +40,2 @@ * Receives an OfflineInterface instance as a prop (presumably from the app | ||
*/ | ||
function OfflineInterfaceProvider({ | ||
@@ -60,3 +68,3 @@ offlineInterface, | ||
return /*#__PURE__*/_react.default.createElement(OfflineContext.Provider, { | ||
return /*#__PURE__*/_react.default.createElement(OfflineInterfaceContext.Provider, { | ||
value: offlineInterface | ||
@@ -74,6 +82,6 @@ }, children); | ||
function useOfflineInterface() { | ||
const offlineInterface = (0, _react.useContext)(OfflineContext); | ||
const offlineInterface = (0, _react.useContext)(OfflineInterfaceContext); | ||
if (offlineInterface === undefined) { | ||
throw new Error('useOfflineInterface must be used within an OfflineInterfaceProvider. Make sure `pwa: { enabled: true }` in `d2.config.js`.'); | ||
throw new Error('Offline interface context not found. If this app is using the app platform, make sure `pwa: { enabled: true }` is in d2.config.js. If this is not a platform app, make sure your app is wrapped with an app-runtime <Provider> or an <OfflineProvider> from app-service-offline.'); | ||
} | ||
@@ -80,0 +88,0 @@ |
@@ -26,3 +26,3 @@ "use strict"; | ||
if (!offlineInterface) { | ||
return children; | ||
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children); | ||
} // If PWA is not enabled, just init interface to make sure new SW gets | ||
@@ -40,3 +40,3 @@ // activated with code that unregisters SW. Not technically necessary if a | ||
}); | ||
return children; | ||
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children); | ||
} | ||
@@ -43,0 +43,0 @@ |
@@ -12,3 +12,2 @@ "use strict"; | ||
// eslint-disable-next-line react/prop-types | ||
const RenderCounter = ({ | ||
@@ -15,0 +14,0 @@ id, |
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } | ||
/* eslint-disable react/prop-types */ | ||
import { act, fireEvent, render, screen } from '@testing-library/react'; | ||
@@ -46,3 +45,4 @@ import React from 'react'; | ||
const TestSection = ({ | ||
id | ||
id, | ||
children | ||
}) => /*#__PURE__*/React.createElement(CacheableSection, { | ||
@@ -56,3 +56,3 @@ id: id, | ||
countsObj: renderCounts | ||
})); | ||
}), children); | ||
@@ -86,3 +86,4 @@ const TestSingleSection = props => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
jest.clearAllMocks() // This syntax appeases typescript: | ||
; | ||
console.error.mockRestore(); | ||
@@ -277,2 +278,54 @@ resetRenderCounts(renderCounts); | ||
}); | ||
}); | ||
describe('useCacheableSection can be used inside a child of CacheableSection', () => { | ||
const ChildTest = props => { | ||
// Props are spread so they can be overwritten | ||
return /*#__PURE__*/React.createElement(OfflineProvider, _extends({ | ||
offlineInterface: mockOfflineInterface | ||
}, props), /*#__PURE__*/React.createElement(TestSection, _extends({ | ||
id: '1' | ||
}, props), /*#__PURE__*/React.createElement(TestControls, _extends({ | ||
id: '1' | ||
}, props)))); | ||
}; | ||
it('handles a successful recording', async done => { | ||
const { | ||
getByTestId, | ||
queryByTestId | ||
} = screen; | ||
const onStarted = () => { | ||
expect(getByTestId(/recording-state/)).toHaveTextContent('recording'); | ||
expect(getByTestId(/loading-mask/)).toBeInTheDocument(); | ||
expect(getByTestId(/section-rc/)).toBeInTheDocument(); | ||
}; | ||
const onCompleted = () => { | ||
expect(getByTestId(/recording-state/)).toHaveTextContent('default'); | ||
expect(queryByTestId(/loading-mask/)).not.toBeInTheDocument(); | ||
done(); | ||
}; | ||
const recordingOptions = { | ||
onStarted, | ||
onCompleted | ||
}; | ||
const makeRecordingHandler = startRecording => { | ||
return () => startRecording(recordingOptions); | ||
}; | ||
render( /*#__PURE__*/React.createElement(ChildTest, { | ||
makeRecordingHandler: makeRecordingHandler | ||
})); | ||
await act(async () => { | ||
fireEvent.click(getByTestId(/start-recording/)); | ||
}); // At this stage, should be pending | ||
// - In this test case, 'controls' should not be rendered | ||
expect(queryByTestId(/recording-state/)).not.toBeInTheDocument(); | ||
expect(queryByTestId(/section-rc/)).not.toBeInTheDocument(); | ||
expect.assertions(7); | ||
}); | ||
}); |
@@ -21,3 +21,4 @@ import { render, screen, waitFor } from '@testing-library/react'; | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
jest.clearAllMocks() // syntax needed to appease typescript | ||
; | ||
console.error.mockRestore(); | ||
@@ -72,3 +73,3 @@ }); | ||
await waitFor(() => getByTestId('sections').textContent !== '{}'); | ||
const textContent = JSON.parse(getByTestId('sections').textContent); | ||
const textContent = JSON.parse(getByTestId('sections').textContent || ''); | ||
expect(textContent).toEqual({ | ||
@@ -87,2 +88,3 @@ 1: { | ||
return /*#__PURE__*/React.createElement(CacheableSection, { | ||
loadingMask: /*#__PURE__*/React.createElement("div", null), | ||
id: 'id' | ||
@@ -89,0 +91,0 @@ }, /*#__PURE__*/React.createElement("div", { |
@@ -11,3 +11,3 @@ import { act, renderHook } from '@testing-library/react-hooks'; | ||
result | ||
} = renderHook((...args) => useOnlineStatus(...args)); | ||
} = renderHook(() => useOnlineStatus()); | ||
expect(result.current.online).toBe(true); | ||
@@ -20,3 +20,3 @@ expect(result.current.offline).toBe(false); | ||
result | ||
} = renderHook((...args) => useOnlineStatus(...args)); | ||
} = renderHook(() => useOnlineStatus()); | ||
expect(result.current.online).toBe(false); | ||
@@ -35,8 +35,6 @@ expect(result.current.offline).toBe(true); | ||
waitForNextUpdate | ||
} = renderHook((...args) => useOnlineStatus(...args)); | ||
} = renderHook(() => useOnlineStatus()); | ||
act(() => { | ||
// Trigger callback captured by addEventListener mock | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.offline(new Event('offline')); | ||
}); // Wait for debounce | ||
@@ -57,7 +55,5 @@ | ||
waitForNextUpdate | ||
} = renderHook((...args) => useOnlineStatus(...args)); | ||
} = renderHook(() => useOnlineStatus()); | ||
act(() => { | ||
events.online({ | ||
type: 'online' | ||
}); | ||
events.online(new Event('online')); | ||
}); // Wait for debounce | ||
@@ -81,14 +77,8 @@ | ||
waitForNextUpdate | ||
} = renderHook((...args) => useOnlineStatus(...args)); | ||
} = renderHook(() => useOnlineStatus()); | ||
await act(async () => { | ||
// Multiple events in succession | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.online({ | ||
type: 'online' | ||
}); | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.offline(new Event('offline')); | ||
events.online(new Event('online')); | ||
events.offline(new Event('offline')); | ||
}); // Immediately, nothing should happen | ||
@@ -118,11 +108,5 @@ | ||
// Multiple events in succession | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.online({ | ||
type: 'online' | ||
}); | ||
events.offline({ | ||
type: 'offline' | ||
}); | ||
events.offline(new Event('offline')); | ||
events.online(new Event('online')); | ||
events.offline(new Event('offline')); | ||
}); // Immediately, nothing should happen | ||
@@ -129,0 +113,0 @@ |
@@ -21,3 +21,4 @@ /* eslint-disable react/display-name, react/prop-types */ | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
jest.clearAllMocks() // This syntax appeases typescript: | ||
; | ||
console.error.mockRestore(); | ||
@@ -24,0 +25,0 @@ }); |
@@ -14,3 +14,2 @@ import PropTypes from 'prop-types'; | ||
*/ | ||
function getSectionsById(sectionsArray) { | ||
@@ -69,7 +68,9 @@ return sectionsArray.reduce((result, { | ||
React.useEffect(() => { | ||
offlineInterface.getCachedSections().then(sections => { | ||
store.mutate(state => ({ ...state, | ||
cachedSections: getSectionsById(sections) | ||
})); | ||
}); | ||
if (offlineInterface) { | ||
offlineInterface.getCachedSections().then(sections => { | ||
store.mutate(state => ({ ...state, | ||
cachedSections: getSectionsById(sections) | ||
})); | ||
}); | ||
} | ||
}, [store, offlineInterface]); | ||
@@ -83,2 +84,3 @@ return /*#__PURE__*/React.createElement(GlobalStateProvider, { | ||
}; | ||
/** | ||
@@ -91,3 +93,2 @@ * Uses an optimized global state to manage 'recording state' values without | ||
*/ | ||
export function useRecordingState(id) { | ||
@@ -131,2 +132,3 @@ const [recordingState] = useGlobalState(state => state.recordingStates[id]); | ||
} | ||
/** | ||
@@ -137,4 +139,2 @@ * Uses global state to manage an object of cached sections' statuses | ||
*/ | ||
export function useCachedSections() { | ||
@@ -168,2 +168,3 @@ const [cachedSections] = useGlobalState(state => state.cachedSections); | ||
} | ||
/** | ||
@@ -176,3 +177,2 @@ * Uses global state to manage the cached status of just one section, which | ||
*/ | ||
export function useCachedSection(id) { | ||
@@ -179,0 +179,0 @@ const [status] = useGlobalState(state => state.cachedSections[id]); |
@@ -11,2 +11,3 @@ import PropTypes from 'prop-types'; | ||
}; | ||
/** | ||
@@ -21,3 +22,2 @@ * Returns the main controls for a cacheable section and manages recording | ||
*/ | ||
export function useCacheableSection(id) { | ||
@@ -37,6 +37,13 @@ const offlineInterface = useOfflineInterface(); | ||
useEffect(() => { | ||
// On mount, add recording state for this ID to context | ||
setRecordingState(recordingStates.default); // On unnmount, remove recording state | ||
// On mount, add recording state for this ID to context if needed | ||
if (!recordingState) { | ||
setRecordingState(recordingStates.default); | ||
} // On unnmount, remove recording state if not recording | ||
return removeRecordingState; | ||
return () => { | ||
if (recordingState && recordingState !== recordingStates.recording && recordingState !== recordingStates.pending) { | ||
removeRecordingState(); | ||
} | ||
}; | ||
}, []); // eslint-disable-line react-hooks/exhaustive-deps | ||
@@ -56,13 +63,13 @@ | ||
recordingTimeoutDelay, | ||
onStarted: (...args) => { | ||
onRecordingStarted(...args); | ||
onStarted && onStarted(...args); | ||
onStarted: () => { | ||
onRecordingStarted(); | ||
onStarted && onStarted(); | ||
}, | ||
onCompleted: (...args) => { | ||
onRecordingCompleted(...args); | ||
onCompleted && onCompleted(...args); | ||
onCompleted: () => { | ||
onRecordingCompleted(); | ||
onCompleted && onCompleted(); | ||
}, | ||
onError: (...args) => { | ||
onRecordingError(...args); | ||
onError && onError(...args); | ||
onError: error => { | ||
onRecordingError(error); | ||
onError && onError(error); | ||
} | ||
@@ -96,2 +103,3 @@ }).then(() => setRecordingState(recordingStates.pending)); | ||
} | ||
/** | ||
@@ -108,3 +116,2 @@ * Used to wrap the relevant component to be recorded and saved offline. | ||
*/ | ||
export function CacheableSection({ | ||
@@ -124,5 +131,8 @@ id, | ||
if (recordingState === recordingStates.error) return children; // Handling rendering with the following conditions prevents an unncessary | ||
if (recordingState === recordingStates.error) { | ||
return /*#__PURE__*/React.createElement(React.Fragment, null, children); | ||
} // Handling rendering with the following conditions prevents an unncessary | ||
// rerender after successful recording | ||
return /*#__PURE__*/React.createElement(React.Fragment, null, recordingState === recordingStates.recording && loadingMask, recordingState !== recordingStates.pending && children); | ||
@@ -129,0 +139,0 @@ } |
import isEqual from 'lodash/isEqual'; | ||
import PropTypes from 'prop-types'; | ||
import React, { useEffect, useCallback, useContext, useState } from 'react'; // This file creates a redux-like state management service using React context | ||
import React, { useEffect, useCallback, useContext, useState } from 'react'; | ||
// This file creates a redux-like state management service using React context | ||
// that minimizes unnecessary rerenders that consume the context. | ||
// See more at https://github.com/amcgee/state-service-poc | ||
const identity = state => state; | ||
@@ -62,3 +63,3 @@ | ||
}; | ||
export const useGlobalStateMutation = mutationCreator => { | ||
export function useGlobalStateMutation(mutationCreator) { | ||
const store = useGlobalStateStore(); | ||
@@ -68,2 +69,2 @@ return useCallback((...args) => { | ||
}, [mutationCreator, store]); | ||
}; | ||
} |
import { useAlert } from '@dhis2/app-service-alerts'; | ||
import PropTypes from 'prop-types'; | ||
import React, { createContext, useContext } from 'react'; | ||
const OfflineContext = /*#__PURE__*/createContext(); | ||
// This is to prevent 'offlineInterface could be null' type-checking errors | ||
const noopOfflineInterface = { | ||
pwaEnabled: false, | ||
init: () => () => null, | ||
startRecording: async () => undefined, | ||
getCachedSections: async () => [], | ||
removeSection: async () => false | ||
}; | ||
const OfflineInterfaceContext = /*#__PURE__*/createContext(noopOfflineInterface); | ||
/** | ||
@@ -13,3 +22,2 @@ * Receives an OfflineInterface instance as a prop (presumably from the app | ||
*/ | ||
export function OfflineInterfaceProvider({ | ||
@@ -40,3 +48,3 @@ offlineInterface, | ||
return /*#__PURE__*/React.createElement(OfflineContext.Provider, { | ||
return /*#__PURE__*/React.createElement(OfflineInterfaceContext.Provider, { | ||
value: offlineInterface | ||
@@ -52,6 +60,6 @@ }, children); | ||
export function useOfflineInterface() { | ||
const offlineInterface = useContext(OfflineContext); | ||
const offlineInterface = useContext(OfflineInterfaceContext); | ||
if (offlineInterface === undefined) { | ||
throw new Error('useOfflineInterface must be used within an OfflineInterfaceProvider. Make sure `pwa: { enabled: true }` in `d2.config.js`.'); | ||
throw new Error('Offline interface context not found. If this app is using the app platform, make sure `pwa: { enabled: true }` is in d2.config.js. If this is not a platform app, make sure your app is wrapped with an app-runtime <Provider> or an <OfflineProvider> from app-service-offline.'); | ||
} | ||
@@ -58,0 +66,0 @@ |
@@ -5,4 +5,4 @@ import PropTypes from 'prop-types'; | ||
import { OfflineInterfaceProvider } from './offline-interface'; | ||
/** A context provider for all the relevant offline contexts */ | ||
export function OfflineProvider({ | ||
@@ -15,3 +15,3 @@ offlineInterface, | ||
if (!offlineInterface) { | ||
return children; | ||
return /*#__PURE__*/React.createElement(React.Fragment, null, children); | ||
} // If PWA is not enabled, just init interface to make sure new SW gets | ||
@@ -29,3 +29,3 @@ // activated with code that unregisters SW. Not technically necessary if a | ||
}); | ||
return children; | ||
return /*#__PURE__*/React.createElement(React.Fragment, null, children); | ||
} | ||
@@ -32,0 +32,0 @@ |
import debounce from 'lodash/debounce'; | ||
import { useState, useEffect, useCallback } from 'react'; | ||
/** | ||
@@ -16,3 +17,2 @@ * Returns the browser's online status. Updates in response to 'online' and | ||
*/ | ||
export function useOnlineStatus(options) { | ||
@@ -19,0 +19,0 @@ // initialize state to `navigator.onLine` value |
@@ -1,3 +0,2 @@ | ||
import React from 'react'; // eslint-disable-next-line react/prop-types | ||
import React from 'react'; | ||
export const RenderCounter = ({ | ||
@@ -4,0 +3,0 @@ id, |
{ | ||
"name": "@dhis2/app-service-offline", | ||
"description": "A runtime service for online/offline detection and offline caching", | ||
"version": "2.10.0-pwa.1", | ||
"version": "2.10.0-pwa.2", | ||
"main": "./build/cjs/index.js", | ||
"module": "./build/es/index.js", | ||
"types": "build/types/index.d.ts", | ||
"exports": { | ||
@@ -25,9 +26,13 @@ "import": "./build/es/index.js", | ||
"scripts": { | ||
"build": "d2-app-scripts build", | ||
"start": "d2-app-scripts start", | ||
"build:types": "tsc --emitDeclarationOnly --outDir ./build/types", | ||
"build:package": "d2-app-scripts build", | ||
"build": "concurrently -n build,types \"yarn build:package\" \"yarn build:types\"", | ||
"watch": "NODE_ENV=development concurrently -n build,types \"yarn build:package --watch\" \"yarn build:types --watch\"", | ||
"type-check": "tsc --noEmit --allowJs --checkJs", | ||
"type-check:watch": "yarn type-check --watch", | ||
"test": "d2-app-scripts test", | ||
"deploy": "d2-app-scripts deploy" | ||
"coverage": "yarn test --coverage" | ||
}, | ||
"peerDependencies": { | ||
"@dhis2/app-service-alerts": "^2.10.0-pwa.1", | ||
"@dhis2/app-service-alerts": "^2.10.0-pwa.2", | ||
"prop-types": "^15.7.2", | ||
@@ -34,0 +39,0 @@ "react": "^16.8.6", |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
115085
44
2862
0