diff --git a/docs/advanced/offline/CustomOnlineStatusMessage.md b/docs/advanced/offline/CustomOnlineStatusMessage.md new file mode 100644 index 000000000..c70b0b805 --- /dev/null +++ b/docs/advanced/offline/CustomOnlineStatusMessage.md @@ -0,0 +1,46 @@ +# Custom Connection Status Message + +By default, the header bar shows either an "Online" or "Offline" message based on whether the app can connect to the DHIS2 server. If it's useful for an app to share other relevant info in that badge, for example "3 changes saved locally", then it's possible to set a custom message there: + +![Custom online status message](custom-online-status-message.png) + +## Hooks + +```ts +import { + useSetOnlineStatusMessage, + useOnlineStatusMessageValue, + useOnlineStatusMessage, +} from '@dhis2/app-runtime' +``` + +### `useSetOnlineStatusMessage` + +It's most likely that you'll only use `useSetOnlineStatusMessage()`, a hook that returns a `setState` function. It can set the value in the Header Bar without causing a rerender for itself. + +`setOnlineStatusMessage()` accepts a `ReactNode`-type object, so icons or other components can be added to the badge, but don't go crazy. A simple string is also a valid `ReactNode`: + +```ts +const setOnlineStatusMessage: (message: ReactNode) => void = + useSetOnlineStatusMessage() +``` + +### `useOnlineStatusMessageValue` + +This hook returns just the value: + +```ts +const onlineStatusMessage: ReactNode = useOnlineStatusMessageValue() +``` + +### `useOnlineStatusMessage` + +This hook returns both the value and the `set` function: + +```ts +const { onlineMessage, setOnlineStatusMessage } = useOnlineStatusMessage() +``` + +## Example + +You can see an example in use in the [Aggregate Data Entry app](https://github.com/dhis2/aggregate-data-entry-app/blob/dadd61392ea010a8017a19a25eaf76f885d9eea7/src/data-workspace/use-handle-headerbar-status.js) diff --git a/docs/advanced/offline/custom-online-status-message.png b/docs/advanced/offline/custom-online-status-message.png new file mode 100644 index 000000000..ac8841614 Binary files /dev/null and b/docs/advanced/offline/custom-online-status-message.png differ diff --git a/docs/advanced/offline/useDhis2ConnectionStatus.md b/docs/advanced/offline/useDhis2ConnectionStatus.md index 6d7ede27f..e9f4ae0bd 100644 --- a/docs/advanced/offline/useDhis2ConnectionStatus.md +++ b/docs/advanced/offline/useDhis2ConnectionStatus.md @@ -6,6 +6,8 @@ This hook is used to detect whether or not the app can connect to the DHIS2 serv It's designed to detect server connection because checking just the internet connection can lead to problems on DHIS2 instances that are implemented offline, where features or actions might be blocked because the device is offline, even though the app can connect to the DHIS2 server just fine. In these cases, what matters is whether or not the app can connect to the DHIS2 server. +This what the DHIS2 Header Bar uses to show an "Online" or "Offline" badge. + ```ts import { useDhis2ConnectionStatus } from '@dhis2/app-runtime' diff --git a/runtime/src/index.ts b/runtime/src/index.ts index 5bf44ba37..1ae215aa2 100644 --- a/runtime/src/index.ts +++ b/runtime/src/index.ts @@ -17,6 +17,8 @@ export { useOnlineStatus, useDhis2ConnectionStatus, useOnlineStatusMessage, + useSetOnlineStatusMessage, + useOnlineStatusMessageValue, useCacheableSection, CacheableSection, useCachedSections, diff --git a/services/offline/src/index.ts b/services/offline/src/index.ts index ef2fbf915..5d7411f1b 100644 --- a/services/offline/src/index.ts +++ b/services/offline/src/index.ts @@ -3,6 +3,10 @@ export { CacheableSection, useCacheableSection } from './lib/cacheable-section' export { useCachedSections } from './lib/cacheable-section-state' // Use "useOnlineStatus" name for backwards compatibility export { useNetworkStatus as useOnlineStatus } from './lib/network-status' -export { useOnlineStatusMessage } from './lib/online-status-message' +export { + useOnlineStatusMessage, + useSetOnlineStatusMessage, + useOnlineStatusMessageValue, +} from './lib/online-status-message' export { clearSensitiveCaches } from './lib/clear-sensitive-caches' export { useDhis2ConnectionStatus } from './lib/dhis2-connection-status' diff --git a/services/offline/src/lib/online-status-message.tsx b/services/offline/src/lib/online-status-message.tsx index 478d668b3..726b41a5e 100644 --- a/services/offline/src/lib/online-status-message.tsx +++ b/services/offline/src/lib/online-status-message.tsx @@ -1,39 +1,47 @@ import React, { ReactElement, ReactNode, useContext, useState } from 'react' -import { OnlineStatusMessageContextAPI } from '../types' -const defaultApi: OnlineStatusMessageContextAPI = { - onlineStatusMessage: undefined, - setOnlineStatusMessage: () => undefined, -} - -const OnlineStatusMessageContext = - React.createContext(defaultApi) - -export const useOnlineStatusMessage = (): OnlineStatusMessageContextAPI => { - const { onlineStatusMessage, setOnlineStatusMessage } = - useContext(OnlineStatusMessageContext) +type SetOnlineStatusMessage = (message: ReactNode) => void - return { - onlineStatusMessage, - setOnlineStatusMessage, - } -} +// 'get' and 'set' contexts are separated so 'setter' consumers that don't +// actually need the value don't have to rerender when the value changes: +const OnlineStatusMessageValueContext = + React.createContext(undefined) +const SetOnlineStatusMessageContext = + React.createContext(() => undefined) export const OnlineStatusMessageProvider = ({ children, }: { children: ReactNode }): ReactElement => { - const [onlineStatusMessage, setOnlineStatusMessage] = useState() + const [onlineStatusMessage, setOnlineStatusMessage] = useState() // note: not undefined return ( - - {children} - + + + {children} + + ) } + +export const useOnlineStatusMessageValue = () => { + return useContext(OnlineStatusMessageValueContext) +} + +export const useSetOnlineStatusMessage = () => { + return useContext(SetOnlineStatusMessageContext) +} + +// combination of both getter and setter (also provides backward compatability) +export const useOnlineStatusMessage = () => { + const onlineStatusMessage = useOnlineStatusMessageValue() + const setOnlineStatusMessage = useSetOnlineStatusMessage() + + return { + onlineStatusMessage, + setOnlineStatusMessage, + } +} diff --git a/services/offline/src/types.ts b/services/offline/src/types.ts index 730d9f13c..37580b406 100644 --- a/services/offline/src/types.ts +++ b/services/offline/src/types.ts @@ -1,5 +1,3 @@ -import { ReactNode } from 'react' - // Cacheable Section types export type RecordingState = 'default' | 'pending' | 'error' | 'recording' @@ -66,10 +64,3 @@ export interface OfflineInterface { getCachedSections: () => Promise removeSection: (id: string) => Promise } - -// Online status types - -export type OnlineStatusMessageContextAPI = { - onlineStatusMessage?: ReactNode - setOnlineStatusMessage: (additionalInfo: ReactNode) => void -}