feat: show new updates in dashboard (#690)

* feat: show new update banner

* feat(http): add request logger

* refactor: updates checker

* feat: make update check optional

* fix: empty releases

* add toggle switch for update checks

* feat: toggle updates check from settings

* feat: toggle updates check from settings

* feat: check on toggle enabled

---------

Co-authored-by: soup <soup@r4tio.dev>
This commit is contained in:
ze0s 2023-02-05 18:44:11 +01:00 committed by GitHub
parent 3fdd7cf5e4
commit 2917a7d42d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 687 additions and 121 deletions

View file

@ -2,11 +2,13 @@ import { Fragment } from "react";
import { Link, NavLink, Outlet } from "react-router-dom";
import { Disclosure, Menu, Transition } from "@headlessui/react";
import { BookOpenIcon, UserIcon } from "@heroicons/react/24/solid";
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
import { Bars3Icon, XMarkIcon, MegaphoneIcon } from "@heroicons/react/24/outline";
import { AuthContext } from "../utils/Context";
import logo from "../logo.png";
import { useQuery } from "react-query";
import { APIClient } from "../api/APIClient";
interface NavItem {
name: string;
@ -27,6 +29,17 @@ export default function Base() {
{ name: "Logs", path: "/logs" }
];
const { data } = useQuery(
["updates"],
() => APIClient.updates.getLatestRelease(),
{
retry: false,
refetchOnWindowFocus: false,
onError: err => console.log(err)
}
);
return (
<div className="min-h-screen">
<Disclosure
@ -185,6 +198,16 @@ export default function Base() {
</div>
</div>
</div>
{data && data.html_url && (
<a href={data.html_url} target="_blank">
<div className="flex mt-4 py-2 bg-blue-500 rounded justify-center">
<MegaphoneIcon className="h-6 w-6 text-blue-100"/>
<span className="text-blue-100 font-medium mx-3">New update available!</span>
<span className="inline-flex items-center rounded-md bg-blue-100 px-2.5 py-0.5 text-sm font-medium text-blue-800">{data?.name}</span>
</div>
</a>
)}
</div>
<Disclosure.Panel className="border-b border-gray-300 dark:border-gray-700 md:hidden">

View file

@ -1,12 +1,17 @@
import { useQuery } from "react-query";
import { useMutation, useQuery } from "react-query";
import { APIClient } from "../../api/APIClient";
import { Checkbox } from "../../components/Checkbox";
import { SettingsContext } from "../../utils/Context";
import { GithubRelease } from "../../types/Update";
import { toast } from "react-hot-toast";
import Toast from "../../components/notifications/Toast";
import { queryClient } from "../../App";
interface RowItemProps {
label: string;
value?: string;
title?: string;
newUpdate?: GithubRelease;
}
const RowItem = ({ label, value, title }: RowItemProps) => {
@ -23,6 +28,25 @@ const RowItem = ({ label, value, title }: RowItemProps) => {
);
};
const RowItemVersion = ({ label, value, title, newUpdate }: RowItemProps) => {
if (!value)
return null;
return (
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
<dt className="font-medium text-gray-500 dark:text-white" title={title}>{label}:</dt>
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">
{value}
{newUpdate && newUpdate.html_url && (
<span>
<a href={newUpdate.html_url} target="_blank"><span className="ml-2 inline-flex items-center rounded-md bg-green-100 px-2.5 py-0.5 text-sm font-medium text-green-800">{newUpdate.name} available!</span></a>
</span>
)}
</dd>
</div>
);
};
function ApplicationSettings() {
const [settings, setSettings] = SettingsContext.use();
@ -36,6 +60,38 @@ function ApplicationSettings() {
}
);
const { data: updateData } = useQuery(
["updates"],
() => APIClient.updates.getLatestRelease(),
{
retry: false,
refetchOnWindowFocus: false,
onError: err => console.log(err)
}
);
const checkUpdateMutation = useMutation(
() => APIClient.updates.check(),
{
onSuccess: () => {
queryClient.invalidateQueries(["updates"]);
}
}
);
const toggleCheckUpdateMutation = useMutation(
(value: boolean) => APIClient.config.update({ check_for_updates: value }),
{
onSuccess: () => {
toast.custom((t) => <Toast type="success" body={"Config successfully updated!"} t={t}/>);
queryClient.invalidateQueries(["config"]);
checkUpdateMutation.mutate();
}
}
);
return (
<div className="divide-y divide-gray-200 dark:divide-gray-700 lg:col-span-9">
<div className="py-6 px-4 sm:p-6 lg:pb-8">
@ -98,7 +154,7 @@ function ApplicationSettings() {
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="px-4 py-5 sm:p-0">
<dl className="sm:divide-y divide-gray-200 dark:divide-gray-700">
<RowItem label="Version" value={data?.version} />
<RowItemVersion label="Version" value={data?.version} newUpdate={updateData ?? undefined} />
<RowItem label="Commit" value={data?.commit} />
<RowItem label="Build date" value={data?.date} />
<RowItem label="Log path" value={data?.log_path} title="Set in config.toml" />
@ -117,6 +173,16 @@ function ApplicationSettings() {
})}
/>
</div>
<div className="px-4 sm:px-6 py-1">
<Checkbox
label="Check for updates"
description="Get notified of new updates."
value={data?.check_for_updates ?? true}
setValue={(newValue: boolean) => {
toggleCheckUpdateMutation.mutate(newValue);
}}
/>
</div>
<div className="px-4 sm:px-6 py-1">
<Checkbox
label="Dark theme"