Skip to content

Commit

Permalink
fix(DHIS2-13915): show spinner when an app is being installed
Browse files Browse the repository at this point in the history
  • Loading branch information
kabaros committed Dec 13, 2023
1 parent a00f1e8 commit 43a8ece
Show file tree
Hide file tree
Showing 5 changed files with 1,172 additions and 50 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@dhis2/app-runtime": "^3.2.0",
"@dhis2/d2-i18n": "^1.1.0",
"@dhis2/prop-types": "^2.0.3",
"@dhis2/ui": "^7.2.0",
"@dhis2/ui": "^9.0.1",
"moment": "^2.29.0",
"query-string": "^6.14.1",
"react-router-dom": "^5.2.0",
Expand Down
37 changes: 24 additions & 13 deletions src/components/AppDetails/ManageInstalledVersion.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useAlert } from '@dhis2/app-runtime'
import i18n from '@dhis2/d2-i18n'
import { PropTypes } from '@dhis2/prop-types'
import { Button } from '@dhis2/ui'
import React from 'react'
import { Button, CircularLoader } from '@dhis2/ui'
import React, { useState } from 'react'
import { useApi } from '../../api.js'
import { getLatestVersion } from '../../get-latest-version.js'
import { semverGt } from '../../semver-gt.js'
Expand Down Expand Up @@ -30,8 +30,12 @@ export const ManageInstalledVersion = ({
const { installVersion, uninstallApp } = useApi()
const successAlert = useAlert(({ message }) => message, { success: true })
const errorAlert = useAlert(({ message }) => message, { critical: true })

const [isInstalling, setIsInstalling] = useState(false)

const handleInstall = async () => {
try {
setIsInstalling(true)
await installVersion(latestVersion.id)
successAlert.show({
message: canUpdate
Expand All @@ -43,14 +47,16 @@ export const ManageInstalledVersion = ({
errorAlert.show({
message: canUpdate
? i18n.t('Failed to update app: {{errorMessage}}', {
errorMessage: error.message,
nsSeparator: '-:-',
})
errorMessage: error.message,
nsSeparator: '-:-',
})
: i18n.t('Failed to install app: {{errorMessage}}', {
errorMessage: error.message,
nsSeparator: '-:-',
}),
errorMessage: error.message,
nsSeparator: '-:-',
}),
})
} finally {
setIsInstalling(false)
}
}
const handleUninstall = async () => {
Expand All @@ -72,12 +78,17 @@ export const ManageInstalledVersion = ({

return (
<div className={styles.manageInstalledVersion}>
{canInstall && (
<>
<Button primary onClick={handleInstall}>
{canUpdate
{canInstall && (<>
<Button primary onClick={handleInstall} disabled={isInstalling}>
{isInstalling && (
<>
{i18n.t('Installing...')}
<CircularLoader small />
</>
)}
{!isInstalling ? canUpdate
? i18n.t('Update to latest version')
: i18n.t('Install')}
: i18n.t('Install') : ''}
</Button>
<span className={styles.manageInstalledVersionDescription}>
{i18n.t('{{channel}} release {{version}}', {
Expand Down
84 changes: 51 additions & 33 deletions src/components/AppDetails/Versions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import i18n from '@dhis2/d2-i18n'
import { PropTypes } from '@dhis2/prop-types'
import {
Checkbox,
CircularLoader,
Button,
Table,
TableHead,
Expand Down Expand Up @@ -86,7 +87,7 @@ ChannelsFilter.propTypes = {
versions: PropTypes.array.isRequired,
}

const VersionsTable = ({ installedVersion, versions, onVersionInstall }) => (
const VersionsTable = ({ installedVersion, versions, onVersionInstall, versionBeingInstalled }) => (
<Table>
<TableHead>
<TableRowHead>
Expand All @@ -101,39 +102,48 @@ const VersionsTable = ({ installedVersion, versions, onVersionInstall }) => (
</TableRowHead>
</TableHead>
<TableBody>
{versions.map((version) => (
<TableRow key={version.id}>
<TableCell>{version.version}</TableCell>
<TableCell>
{channelToDisplayName[version.channel]}
</TableCell>
<TableCell>
{moment(version.created).format('ll')}
</TableCell>
<TableCell>
<Button
small
secondary
className={styles.installBtn}
disabled={version.version === installedVersion}
onClick={() => onVersionInstall(version)}
>
{version.version === installedVersion
? i18n.t('Installed')
: i18n.t('Install')}
</Button>
<a
download
href={version.downloadUrl}
className={styles.downloadLink}
>
<Button small secondary>
{i18n.t('Download')}
{versions.map((version) => {
const isVersionInstalling = versionBeingInstalled === version.id
return (
<TableRow key={version.id}>
<TableCell>{version.version}</TableCell>
<TableCell>
{channelToDisplayName[version.channel]}
</TableCell>
<TableCell>
{moment(version.created).format('ll')}
</TableCell>
<TableCell>
<Button
small
secondary
className={styles.installBtn}
disabled={version.version === installedVersion || versionBeingInstalled}
onClick={() => onVersionInstall(version)}
>
{isVersionInstalling && (
<>
{i18n.t('Installing...')}
<CircularLoader small />
</>
)}
{!isVersionInstalling ? version.version === installedVersion
? i18n.t('Installed')
: i18n.t('Install') : ''}
</Button>
</a>
</TableCell>
</TableRow>
))}
<a
download
href={version.downloadUrl}
className={styles.downloadLink}
>
<Button small secondary>
{i18n.t('Download')}
</Button>
</a>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
)
Expand All @@ -142,10 +152,13 @@ VersionsTable.propTypes = {
versions: PropTypes.array.isRequired,
onVersionInstall: PropTypes.func.isRequired,
installedVersion: PropTypes.string,
versionBeingInstalled: PropTypes.string
}

export const Versions = ({ installedVersion, versions, onVersionInstall }) => {
const [channelsFilter, setChannelsFilter] = useState(new Set(['stable']))
const [versionBeingInstalled, setVersionBeingInstalled] = useState(null)

const installSuccessAlert = useAlert(i18n.t('App installed successfully'), {
success: true,
})
Expand Down Expand Up @@ -180,13 +193,17 @@ export const Versions = ({ installedVersion, versions, onVersionInstall }) => {
const filteredVersions = versions
.filter((version) => channelsFilter.has(version.channel))
.filter(satisfiesDhisVersion)

const handleVersionInstall = async (version) => {
try {
setVersionBeingInstalled(version.id)
await installVersion(version.id)
installSuccessAlert.show()
onVersionInstall()
} catch (error) {
installErrorAlert.show({ error })
} finally {
setVersionBeingInstalled(null)
}
}

Expand All @@ -199,6 +216,7 @@ export const Versions = ({ installedVersion, versions, onVersionInstall }) => {
/>
{filteredVersions.length > 0 ? (
<VersionsTable
versionBeingInstalled={versionBeingInstalled}
installedVersion={installedVersion}
versions={filteredVersions}
onVersionInstall={handleVersionInstall}
Expand Down
4 changes: 4 additions & 0 deletions src/pages/AppHubApp/AppHubApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export const AppHubApp = ({ match }) => {
</NoticeBox>
)
}

// ToDo: This check here is the cause of the bug https://dhis2.atlassian.net/browse/DHIS2-15586
// custom apps seems to not have an app_hub_id, when these are surfaced then this should be resolved
// otherwise we need to find a different way to match the app ( || app.name === appHubApp.name would work but not reliable)
const installedApp = installedApps.find(
(app) => app.app_hub_id === appHubId
)
Expand Down
Loading

0 comments on commit 43a8ece

Please sign in to comment.