You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
overseerr/src/components/Settings/SettingsAbout/Releases/index.tsx

163 lines
4.8 KiB

import Badge from '@app/components/Common/Badge';
import Button from '@app/components/Common/Button';
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
import Modal from '@app/components/Common/Modal';
import globalMessages from '@app/i18n/globalMessages';
import { Transition } from '@headlessui/react';
import { DocumentTextIcon } from '@heroicons/react/outline';
import dynamic from 'next/dynamic';
import { Fragment, useState } from 'react';
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
import useSWR from 'swr';
// dyanmic is having trouble extracting the props for react-markdown here so we are just ignoring it since its really
// only children we are using
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const ReactMarkdown = dynamic<any>(() => import('react-markdown'), {
ssr: false,
});
const messages = defineMessages({
releases: 'Releases',
releasedataMissing: 'Release data is currently unavailable.',
versionChangelog: '{version} Changelog',
viewongithub: 'View on GitHub',
latestversion: 'Latest',
currentversion: 'Current',
viewchangelog: 'View Changelog',
});
const REPO_RELEASE_API =
'https://api.github.com/repos/sct/overseerr/releases?per_page=20';
interface GitHubRelease {
url: string;
assets_url: string;
upload_url: string;
html_url: string;
id: number;
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
draft: boolean;
prerelease: boolean;
created_at: string;
published_at: string;
tarball_url: string;
zipball_url: string;
body: string;
}
interface ReleaseProps {
release: GitHubRelease;
isLatest: boolean;
currentVersion: string;
}
const Release = ({ currentVersion, release, isLatest }: ReleaseProps) => {
const intl = useIntl();
const [isModalOpen, setModalOpen] = useState(false);
return (
<div className="flex w-full flex-col space-y-3 rounded-md bg-gray-800 px-4 py-2 shadow-md ring-1 ring-gray-700 sm:flex-row sm:space-y-0 sm:space-x-3">
<Transition
as={Fragment}
enter="opacity-0 transition duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="opacity-100 transition duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
show={isModalOpen}
>
<Modal
onCancel={() => setModalOpen(false)}
title={intl.formatMessage(messages.versionChangelog, {
version: release.name,
})}
cancelText={intl.formatMessage(globalMessages.close)}
okText={intl.formatMessage(messages.viewongithub)}
onOk={() => {
window.open(release.html_url, '_blank');
}}
>
<div className="prose">
<ReactMarkdown>{release.body}</ReactMarkdown>
</div>
</Modal>
</Transition>
<div className="flex w-full flex-grow items-center justify-center space-x-2 truncate sm:justify-start">
<span className="truncate text-lg font-bold">
<span className="mr-2 whitespace-nowrap text-xs font-normal">
<FormattedRelativeTime
value={Math.floor(
(new Date(release.created_at).getTime() - Date.now()) / 1000
)}
updateIntervalInSeconds={1}
numeric="auto"
/>
</span>
{release.name}
</span>
{isLatest && (
<Badge badgeType="success">
{intl.formatMessage(messages.latestversion)}
</Badge>
)}
{release.name.includes(currentVersion) && (
<Badge badgeType="primary">
{intl.formatMessage(messages.currentversion)}
</Badge>
)}
</div>
<Button buttonType="primary" onClick={() => setModalOpen(true)}>
<DocumentTextIcon />
<span>{intl.formatMessage(messages.viewchangelog)}</span>
</Button>
</div>
);
};
interface ReleasesProps {
currentVersion: string;
}
const Releases = ({ currentVersion }: ReleasesProps) => {
const intl = useIntl();
const { data, error } = useSWR<GitHubRelease[]>(REPO_RELEASE_API);
if (!data && !error) {
return <LoadingSpinner />;
}
if (!data) {
return (
<div className="text-gray-300">
{intl.formatMessage(messages.releasedataMissing)}
</div>
);
}
return (
<div>
<h3 className="heading">{intl.formatMessage(messages.releases)}</h3>
<div className="section space-y-3">
{data.map((release, index) => {
return (
<div key={`release-${release.id}`}>
<Release
release={release}
currentVersion={currentVersion}
isLatest={index === 0}
/>
</div>
);
})}
</div>
</div>
);
};
export default Releases;