Skip to content

Commit

Permalink
feat: implement shared dashboard plugin wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
jenniferarnesen committed Jan 24, 2025
1 parent ede2fd5 commit 6f32614
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 13 deletions.
96 changes: 96 additions & 0 deletions src/components/DashboardPluginWrapper/DashboardPluginWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
useCacheableSection,
CacheableSection,
useConfig,
} from '@dhis2/app-runtime'
import { CenteredContent, CircularLoader, CssVariables, Layer } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React, { useEffect } from 'react'
import { getPWAInstallationStatus } from '../../modules/getPWAInstallationStatus.js'

const LoadingMask = () => {
return (
<Layer>
<CenteredContent>
<CircularLoader />
</CenteredContent>
</Layer>
)
}

const CacheableSectionWrapper = ({ id, children, isParentCached }) => {
const { startRecording, isCached, remove } = useCacheableSection(id)

useEffect(() => {
if (isParentCached && !isCached) {
startRecording({ onError: console.error })
} else if (!isParentCached && isCached) {
// Synchronize cache state on load or prop update
// -- a back-up to imperative `removeCachedData`
remove()
}
}, [isCached, isParentCached, remove, startRecording])

return (
<CacheableSection id={id} loadingMask={<LoadingMask />}>
{children}
</CacheableSection>
)
}

CacheableSectionWrapper.propTypes = {
children: PropTypes.node,
id: PropTypes.string,
isParentCached: PropTypes.bool,
}

export const DashboardPluginWrapper = ({
onInstallationStatusChange,
children,
cacheId,
isParentCached,
...props
}) => {
const { pwaEnabled } = useConfig()

useEffect(() => {
// Get & send PWA installation status now
getPWAInstallationStatus({
onStateChange: onInstallationStatusChange,
}).then(onInstallationStatusChange)
}, [onInstallationStatusChange])

return props ? (
<div
style={{
display: 'flex',
height: '100%',
overflow: 'hidden',
}}
>
{pwaEnabled ? (
<CacheableSectionWrapper
id={cacheId}
isParentCached={isParentCached}
>
{children(props)}
</CacheableSectionWrapper>
) : (
children(props)
)}
<CssVariables colors spacers elevations />
</div>
) : null
}

DashboardPluginWrapper.defaultProps = {
isParentCached: false,
onInstallationStatusChange: Function.prototype,
}

DashboardPluginWrapper.propTypes = {
cacheId: PropTypes.string,
children: PropTypes.func,
isParentCached: PropTypes.bool,
onInstallationStatusChange: PropTypes.func,
}
3 changes: 1 addition & 2 deletions src/components/Toolbar/HoverMenuBar/HoverMenuDropdown.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Popper } from '@dhis2-ui/popper'
import { Portal } from '@dhis2-ui/portal'
import { Popper, Portal } from '@dhis2/ui'
import cx from 'classnames'
import PropTypes from 'prop-types'
import React, { useRef } from 'react'
Expand Down
2 changes: 1 addition & 1 deletion src/components/Toolbar/HoverMenuBar/HoverMenuList.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colors, elevations, spacers } from '@dhis2/ui-constants'
import { colors, elevations, spacers } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React, { createContext, useCallback, useContext, useState } from 'react'
import { useHoverMenubarContext } from './HoverMenuBar.js'
Expand Down
4 changes: 1 addition & 3 deletions src/components/Toolbar/HoverMenuBar/HoverMenuListItem.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { IconChevronRight24 } from '@dhis2/ui-icons'
import { Popper } from '@dhis2-ui/popper'
import { Portal } from '@dhis2-ui/portal'
import { IconChevronRight24, Popper, Portal } from '@dhis2/ui'
import cx from 'classnames'
import PropTypes from 'prop-types'
import React, { useRef } from 'react'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colors, spacers } from '@dhis2/ui-constants'
import { colors, spacers } from '@dhis2/ui'
import css from 'styled-jsx/css'

export default css`
Expand Down
2 changes: 1 addition & 1 deletion src/components/Toolbar/MenuButton.styles.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colors, spacers, theme } from '@dhis2/ui-constants'
import { colors, spacers, theme } from '@dhis2/ui'
import css from 'styled-jsx/css'

export default css`
Expand Down
2 changes: 1 addition & 1 deletion src/components/Toolbar/Toolbar.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colors } from '@dhis2/ui-constants'
import { colors } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React from 'react'

Expand Down
2 changes: 1 addition & 1 deletion src/components/Toolbar/ToolbarSidebar.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colors } from '@dhis2/ui-constants'
import { colors } from '@dhis2/ui'
import cx from 'classnames'
import PropTypes from 'prop-types'
import React from 'react'
Expand Down
4 changes: 1 addition & 3 deletions src/components/Toolbar/UpdateButton.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import i18n from '@dhis2/d2-i18n'
import { colors } from '@dhis2/ui-constants'
import { IconSync16 } from '@dhis2/ui-icons'
import { CircularLoader } from '@dhis2-ui/loader'
import { CircularLoader, IconSync16, colors } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React from 'react'
import menuButtonStyles from './MenuButton.styles.js'
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export {

export * from './components/RichText/index.js'

export { DashboardPluginWrapper } from './components/DashboardPluginWrapper/DashboardPluginWrapper.js'

// Api

export { default as Analytics } from './api/analytics/Analytics.js'
Expand Down
63 changes: 63 additions & 0 deletions src/modules/getPWAInstallationStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
export const INSTALLATION_STATES = {
READY: 'READY',
INSTALLING: 'INSTALLING',
}

function handleInstallingWorker({ installingWorker, onStateChange }) {
installingWorker.onstatechange = () => {
if (installingWorker.state === 'activated') {
// ... and update state to 'ready'
onStateChange(INSTALLATION_STATES.READY)
}
}
}

/**
* Gets the current installation state of the PWA features, which is intended
* to be reported from this plugin to the parent app to indicate that the
* static assets are cached and ready to be accessed locally instead of over
* the network.
*
* Returns either READY, INSTALLING, or `null` for not installed/won't install
*/
export async function getPWAInstallationStatus({ onStateChange }) {
if (!navigator.serviceWorker) {
// Nothing to do here
return null
}

const registration = await navigator.serviceWorker.getRegistration()
if (!registration) {
// This shouldn't happen since this is a PWA app, but return null
return null
}

if (registration.active) {
return INSTALLATION_STATES.READY
}
// note that 'registration.waiting' is skipped - it implies there's an active one
if (registration.installing) {
handleInstallingWorker({
installingWorker: registration.installing,
onStateChange,
})
return INSTALLATION_STATES.INSTALLING
}

// It shouldn't normally be possible to get here, but just in case,
// listen for installations
registration.onupdatefound = () => {
// update state for this plugin to 'installing'
onStateChange(INSTALLATION_STATES.INSTALLING)

// also listen for the installing worker to become active
const installingWorker = registration.installing
if (!installingWorker) {
return
}
handleInstallingWorker({ installingWorker, onStateChange })
}

// and in the mean time, return null to show 'not installed'
return null
}

0 comments on commit 6f32614

Please sign in to comment.