2025-04-01 10:38:02 +09:00

188 lines
5.4 KiB
TypeScript

import { css } from '@emotion/css';
import { useEffect, useState } from 'react';
import { gt, valid } from 'semver';
import { GrafanaTheme2 } from '@grafana/data';
import { config, reportInteraction } from '@grafana/runtime';
import { Badge, Button, ConfirmModal, Icon, Spinner, useStyles2 } from '@grafana/ui';
import { t } from 'app/core/internationalization';
import { isPreinstalledPlugin } from '../helpers';
import { useInstall } from '../state/hooks';
import { PluginStatus, Version } from '../types';
const PLUGINS_VERSION_PAGE_UPGRADE_INTERACTION_EVENT_NAME = 'plugins_upgrade_clicked';
const PLUGINS_VERSION_PAGE_CHANGE_INTERACTION_EVENT_NAME = 'plugins_downgrade_clicked';
interface Props {
pluginId: string;
version: Version;
latestCompatibleVersion?: string;
installedVersion?: string;
disabled: boolean;
tooltip?: string;
onConfirmInstallation: () => void;
}
export const VersionInstallButton = ({
pluginId,
version,
latestCompatibleVersion,
installedVersion,
disabled,
tooltip,
onConfirmInstallation,
}: Props) => {
const install = useInstall();
const [isInstalling, setIsInstalling] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const styles = useStyles2(getStyles);
const installState = getInstallState(installedVersion, version.version);
useEffect(() => {
if (installedVersion === version.version) {
setIsInstalling(false);
setIsModalOpen(false);
}
}, [installedVersion, version.version]);
if (version.version === installedVersion) {
return <Badge className={styles.badge} text="Installed" icon="check" color="green" />;
}
const performInstallation = () => {
const trackProps = {
path: location.pathname,
plugin_id: pluginId,
version: version.version,
is_latest: latestCompatibleVersion === version.version,
creator_team: 'grafana_plugins_catalog',
schema_version: '1.0.0',
};
if (installState === PluginStatus.UPDATE) {
reportInteraction(PLUGINS_VERSION_PAGE_UPGRADE_INTERACTION_EVENT_NAME, trackProps);
} else {
reportInteraction(PLUGINS_VERSION_PAGE_CHANGE_INTERACTION_EVENT_NAME, {
...trackProps,
previous_version: installedVersion,
});
}
install(pluginId, version.version, installState);
setIsInstalling(true);
onConfirmInstallation();
};
const onInstallClick = () => {
if (installState === PluginStatus.DOWNGRADE) {
setIsModalOpen(true);
} else {
performInstallation();
}
};
const onConfirm = () => {
performInstallation();
};
const onDismiss = () => {
setIsModalOpen(false);
};
const isPreinstalled = isPreinstalledPlugin(pluginId);
const hidden = getButtonHiddenState(installState, isPreinstalled);
return (
<>
<Button
fill="solid"
disabled={disabled || isInstalling}
fullWidth
size="sm"
variant={latestCompatibleVersion === version.version ? 'primary' : 'secondary'}
onClick={onInstallClick}
className={styles.button}
hidden={hidden}
tooltip={tooltip}
tooltipPlacement="bottom-start"
>
{getLabel(installState)}{' '}
{isInstalling ? <Spinner className={styles.spinner} inline size="sm" /> : getIcon(installState)}
</Button>
<ConfirmModal
isOpen={isModalOpen}
title={t('plugins.catalog.versions.downgrade-title', 'Downgrade plugin version')}
body={`${t('plugins.catalog.versions.confirmation-text-1', 'Are you really sure you want to downgrade to version')} ${version.version}? ${t('plugins.catalog.versions.confirmation-text-2', 'You should normally not be doing this')}`}
confirmText={t('plugins.catalog.versions.downgrade-confirm', 'Downgrade')}
onConfirm={onConfirm}
onDismiss={onDismiss}
disabled={isInstalling}
confirmButtonVariant="primary"
/>
</>
);
};
function getLabel(installState: PluginStatus) {
switch (installState) {
case PluginStatus.INSTALL:
return 'Install';
case PluginStatus.UPDATE:
return 'Upgrade';
case PluginStatus.DOWNGRADE:
return 'Downgrade';
default:
return '';
}
}
function getIcon(installState: PluginStatus) {
if (installState === PluginStatus.DOWNGRADE) {
return <Icon name="arrow-down" />;
}
if (installState === PluginStatus.UPDATE) {
return <Icon name="arrow-up" />;
}
return '';
}
function getInstallState(installedVersion?: string, version?: string): PluginStatus {
if (!installedVersion || !version || !valid(installedVersion) || !valid(version)) {
return PluginStatus.INSTALL;
}
return gt(installedVersion, version) ? PluginStatus.DOWNGRADE : PluginStatus.UPDATE;
}
function getButtonHiddenState(installState: PluginStatus, isPreinstalled: { found: boolean; withVersion: boolean }) {
// Default state for initial install
if (installState === PluginStatus.INSTALL) {
return false;
}
// Handle downgrade case
if (installState === PluginStatus.DOWNGRADE) {
return isPreinstalled.found && Boolean(config.featureToggles.preinstallAutoUpdate);
}
// Handle upgrade case
return isPreinstalled.withVersion;
}
const getStyles = (theme: GrafanaTheme2) => ({
spinner: css({
marginLeft: theme.spacing(1),
}),
successIcon: css({
color: theme.colors.success.main,
}),
button: css({
width: theme.spacing(13),
}),
badge: css({
width: theme.spacing(13),
justifyContent: 'center',
}),
});