Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: prep for global shell [DHIS2-15635] #829

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions adapter/src/components/AppWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Alerts } from './Alerts.js'
import { ConnectedHeaderBar } from './ConnectedHeaderBar.js'
import { ErrorBoundary } from './ErrorBoundary.js'
import { LoadingMask } from './LoadingMask.js'
import { PluginPWAUpdateManager } from './PluginPWAUpdateManager.js'
import { styles } from './styles/AppWrapper.style.js'

const AppWrapper = ({
Expand All @@ -14,6 +15,7 @@ const AppWrapper = ({
onPluginError,
clearPluginError,
direction: configDirection,
reportPWAUpdateStatus,
}) => {
const { loading: localeLoading, direction: localeDirection } =
useCurrentUserLocale(configDirection)
Expand All @@ -27,6 +29,9 @@ const AppWrapper = ({
return (
<div className="app-shell-adapter">
<style jsx>{styles}</style>
<PluginPWAUpdateManager
reportPWAUpdateStatus={reportPWAUpdateStatus}
/>
<div className="app-shell-app">
<ErrorBoundary
plugin={true}
Expand Down Expand Up @@ -67,6 +72,7 @@ AppWrapper.propTypes = {
clearPluginError: PropTypes.func,
direction: PropTypes.oneOf(['ltr', 'rtl', 'auto']),
plugin: PropTypes.bool,
reportPWAUpdateStatus: PropTypes.func,
onPluginError: PropTypes.func,
}

Expand Down
2 changes: 1 addition & 1 deletion adapter/src/components/ConnectedHeaderBar.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useConfig } from '@dhis2/app-runtime'
import { usePWAUpdateState } from '@dhis2/pwa'
import { HeaderBar } from '@dhis2/ui'
import React from 'react'
import { usePWAUpdateState } from '../utils/usePWAUpdateState'
import { ConfirmUpdateModal } from './ConfirmUpdateModal'

/**
Expand Down
18 changes: 0 additions & 18 deletions adapter/src/components/OfflineInterfaceContext.js

This file was deleted.

2 changes: 1 addition & 1 deletion adapter/src/components/PWALoadingBoundary.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {
useOfflineInterface,
REGISTRATION_STATE_WAITING,
REGISTRATION_STATE_FIRST_ACTIVATION,
} from '@dhis2/pwa'
import PropTypes from 'prop-types'
import { useEffect, useState } from 'react'
import { useOfflineInterface } from './OfflineInterfaceContext'

export const PWALoadingBoundary = ({ children }) => {
const [pwaReady, setPWAReady] = useState(false)
Expand Down
48 changes: 48 additions & 0 deletions adapter/src/components/PluginPWAUpdateManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { usePWAUpdateState } from '@dhis2/pwa'
import PropTypes from 'prop-types'
import React, { useEffect } from 'react'
import { ConfirmUpdateModal } from './ConfirmUpdateModal'

/**
* Analogous to the ConnectedHeaderbar, for use in plugins since they don't
* use a header bar. See the ConnectedHeaderBar for more
*/

/**
* Check for SW updates or a first activation, sending a message to the host
* app if an update is ready. When an update is applied, if there are
* multiple tabs of this app open, there's anadditional warning step because all
* clients of the service worker will reload when there's an update, which may
* cause data loss.
*/

export function PluginPWAUpdateManager({ reportPWAUpdateStatus }) {
const {
updateAvailable,
confirmReload,
confirmationRequired,
clientsCount,
onConfirmUpdate,
onCancelUpdate,
} = usePWAUpdateState()

useEffect(() => {
if (reportPWAUpdateStatus) {
reportPWAUpdateStatus({
updateAvailable,
onApplyUpdate: updateAvailable ? confirmReload : null,
})
}
}, [updateAvailable, confirmReload, reportPWAUpdateStatus])

return confirmationRequired ? (
<ConfirmUpdateModal
clientsCount={clientsCount}
onConfirm={onConfirmUpdate}
onCancel={onCancelUpdate}
/>
) : null
}
PluginPWAUpdateManager.propTypes = {
reportPWAUpdateStatus: PropTypes.func,
}
15 changes: 12 additions & 3 deletions adapter/src/components/ServerVersionProvider.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { Provider } from '@dhis2/app-runtime'
import { getBaseUrlByAppName, setBaseUrlByAppName } from '@dhis2/pwa'
import {
getBaseUrlByAppName,
setBaseUrlByAppName,
useOfflineInterface,
} from '@dhis2/pwa'
import PropTypes from 'prop-types'
import React, { useEffect, useState } from 'react'
import { get } from '../utils/api.js'
import { parseDHIS2ServerVersion, parseVersion } from '../utils/parseVersion.js'
import { LoadingMask } from './LoadingMask.js'
import { LoginModal } from './LoginModal.js'
import { useOfflineInterface } from './OfflineInterfaceContext.js'

// Save this location so that it's usable after client-side navigations
const originalWindowLocation = new URL(window.location)

export const ServerVersionProvider = ({
appName,
Expand Down Expand Up @@ -178,6 +184,9 @@ export const ServerVersionProvider = ({
return <LoadingMask />
}

// Make sure the base URL is absolute to avoid errors with relative URLs after
// client-side navigation/route changes
const absoluteBaseUrl = new URL(baseUrl, originalWindowLocation).href
const serverVersion = parseDHIS2ServerVersion(systemInfo.version)
const realApiVersion = serverVersion.minor

Expand All @@ -186,7 +195,7 @@ export const ServerVersionProvider = ({
config={{
appName,
appVersion: parseVersion(appVersion),
baseUrl,
baseUrl: absoluteBaseUrl,
apiVersion: apiVersion || realApiVersion,
serverVersion,
systemInfo,
Expand Down
6 changes: 4 additions & 2 deletions adapter/src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { checkForSWUpdateAndReload } from '@dhis2/pwa'
import { checkForSWUpdateAndReload, OfflineInterfaceProvider } from '@dhis2/pwa'
import PropTypes from 'prop-types'
import React from 'react'
import { AppWrapper } from './components/AppWrapper.js'
import { ErrorBoundary } from './components/ErrorBoundary.js'
import { LoginAppWrapper } from './components/LoginAppWrapper.js'
import { OfflineInterfaceProvider } from './components/OfflineInterfaceContext.js'
import { PWALoadingBoundary } from './components/PWALoadingBoundary.js'
import { ServerVersionProvider } from './components/ServerVersionProvider.js'

Expand All @@ -20,6 +19,7 @@ const AppAdapter = ({
showAlertsInPlugin,
onPluginError,
clearPluginError,
reportPWAUpdateStatus,
loginApp,
children,
}) => {
Expand Down Expand Up @@ -71,6 +71,7 @@ const AppAdapter = ({
onPluginError={onPluginError}
clearPluginError={clearPluginError}
direction={direction}
reportPWAUpdateStatus={reportPWAUpdateStatus}
>
{children}
</AppWrapper>
Expand All @@ -92,6 +93,7 @@ AppAdapter.propTypes = {
parentAlertsAdd: PropTypes.func,
plugin: PropTypes.bool,
pwaEnabled: PropTypes.bool,
reportPWAUpdateStatus: PropTypes.func,
showAlertsInPlugin: PropTypes.bool,
url: PropTypes.string,
onPluginError: PropTypes.func,
Expand Down
2 changes: 2 additions & 0 deletions cli/src/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ const handler = async ({
await generateManifests(paths, config, process.env.PUBLIC_URL)

reporter.info('Building appShell...')
// todo: handle pluginified app

// These imports are done asynchronously to allow Vite to use its
// ESM build of its Node API (the CJS build will be removed in v6)
// https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated
Expand Down
5 changes: 5 additions & 0 deletions cli/src/commands/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const handler = async ({
shell: shellSource,
proxy,
proxyPort,
pluginifyApp, // todo: workshop this flag name
host,
allowJsxInJs,
}) => {
Expand Down Expand Up @@ -136,6 +137,10 @@ const handler = async ({
reporter.print('')
reporter.info('Starting development server...')

if (pluginifyApp) {
// todo: handle pluginified app (and in 'location' below too)
}

// These imports are done asynchronously to allow Vite to use its
// ESM build of its Node API (the CJS build will be removed in v6)
// https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated
Expand Down
2 changes: 2 additions & 0 deletions cli/src/lib/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ module.exports = (cwd = process.cwd(), { typeScript } = {}) => {
shell: path.join(base, './.d2/shell'),
shellSrc: path.join(base, './.d2/shell/src'),
shellAppEntrypoint: path.join(base, './.d2/shell/src/App.jsx'),
shellAppBundleEntrypoint: path.join(base, './.d2/shell/src/index.jsx'),
shellAppDirname,
shellApp: path.join(base, `./.d2/shell/${shellAppDirname}`),
shellIndexHtml: path.join(base, './.d2/shell/index.html'),
Expand Down Expand Up @@ -154,6 +155,7 @@ module.exports = (cwd = process.cwd(), { typeScript } = {}) => {

launchPath: 'index.html',
pluginLaunchPath: 'plugin.html',
pluginifiedAppLaunchPath: 'app.html',
}

reporter.debug('PATHS', paths)
Expand Down
5 changes: 5 additions & 0 deletions global-shell/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const { config } = require('@dhis2/cli-style')

module.exports = {
extends: [config.eslintReact],
}
6 changes: 6 additions & 0 deletions global-shell/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# DHIS2 Platform
node_modules
.d2
src/locales
build
.env*
5 changes: 5 additions & 0 deletions global-shell/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const { config } = require('@dhis2/cli-style')

module.exports = {
...require(config.prettier),
}
45 changes: 45 additions & 0 deletions global-shell/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
This project was bootstrapped with [DHIS2 Application Platform](https://github.com/dhis2/app-platform).

## Available Scripts

In the project directory, you can run:

### `yarn start`

Runs the app in the development mode.<br />
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

The page will reload if you make edits.<br />
You will also see any lint errors in the console.

### `yarn test`

Launches the test runner and runs all available tests found in `/src`.<br />

See the section about [running tests](https://platform.dhis2.nu/#/scripts/test) for more information.

### `yarn build`

Builds the app for production to the `build` folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.<br />
A deployable `.zip` file can be found in `build/bundle`!

See the section about [building](https://platform.dhis2.nu/#/scripts/build) for more information.

### `yarn deploy`

Deploys the built app in the `build` folder to a running DHIS2 instance.<br />
This command will prompt you to enter a server URL as well as the username and password of a DHIS2 user with the App Management authority.<br/>
You must run `yarn build` before running `yarn deploy`.<br />

See the section about [deploying](https://platform.dhis2.nu/#/scripts/deploy) for more information.

## Learn More

You can learn more about the platform in the [DHIS2 Application Platform Documentation](https://platform.dhis2.nu/).

You can learn more about the runtime in the [DHIS2 Application Runtime Documentation](https://runtime.dhis2.nu/).

To learn React, check out the [React documentation](https://reactjs.org/).
9 changes: 9 additions & 0 deletions global-shell/d2.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const config = {
type: 'app',

entryPoints: {
app: './src/App.js',
},
}

module.exports = config
15 changes: 15 additions & 0 deletions global-shell/i18n/en.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
msgid ""
msgstr ""
"Project-Id-Version: i18next-conv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-02-02T18:07:52.313Z\n"
"PO-Revision-Date: 2024-02-02T18:07:52.313Z\n"

msgid "Hello {{name}}"
msgstr "Hello {{name}}"

msgid "Welcome to DHIS2!"
msgstr "Welcome to DHIS2!"
35 changes: 35 additions & 0 deletions global-shell/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "global-shell",
"version": "1.0.0",
"description": "",
"license": "BSD-3-Clause",
"private": true,
"scripts": {
"build": "d2-app-scripts build",
"start": "d2-app-scripts start",
"test": "d2-app-scripts test",
"deploy": "D2_PASSWORD=district d2-app-scripts deploy http://localhost:8080 --username admin",
"postinstall": "patch-package",
"bd": "yarn build && yarn deploy",
"del-plugin": "rm -rf node_modules/@dhis2/app-service-plugin/build",
"del-offline": "rm -rf node_modules/@dhis2/app-service-offline/build",
"del-nm-cache": "rm -rf .d2/shell/node_modules/.cache",
"cp-plugin": "cp -r ../app-runtime/services/plugin/build node_modules/@dhis2/app-service-plugin/build",
"cp-offline": "cp -r ../app-runtime/services/offline/build node_modules/@dhis2/app-service-offline/build",
"import-plugin": "yarn del-plugin && yarn del-nm-cache && yarn cp-plugin",
"import-offline": "yarn del-offline && yarn del-nm-cache && yarn cp-offline"
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@dhis2/cli-app-scripts": "^10.4.1",
"@dhis2/cli-style": "^10.5.1",
"patch-package": "^8.0.0",
"postinstall-postinstall": "^2.1.0"
},
"dependencies": {
"@dhis2/app-runtime": "^3.10.2",
"@dhis2/pwa": "10.4.1",
"@dhis2/ui": "^8.16.0",
"react-router-dom": "^6.22.0"
}
}
Loading
Loading