summaryrefslogtreecommitdiffhomepage
path: root/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/Router/index.tsx8
-rw-r--r--frontend/src/apis/hooks/system.ts41
-rw-r--r--frontend/src/apis/queries/keys.ts1
-rw-r--r--frontend/src/apis/raw/system.ts13
-rw-r--r--frontend/src/pages/System/Announcements/index.tsx24
-rw-r--r--frontend/src/pages/System/Announcements/table.tsx91
-rw-r--r--frontend/src/pages/System/system.test.tsx5
-rw-r--r--frontend/src/types/api.d.ts1
-rw-r--r--frontend/src/types/form.d.ts4
-rw-r--r--frontend/src/types/system.d.ts8
10 files changed, 194 insertions, 2 deletions
diff --git a/frontend/src/Router/index.tsx b/frontend/src/Router/index.tsx
index 5e90290c8..d13ea1417 100644
--- a/frontend/src/Router/index.tsx
+++ b/frontend/src/Router/index.tsx
@@ -23,6 +23,7 @@ import SettingsSchedulerView from "@/pages/Settings/Scheduler";
import SettingsSonarrView from "@/pages/Settings/Sonarr";
import SettingsSubtitlesView from "@/pages/Settings/Subtitles";
import SettingsUIView from "@/pages/Settings/UI";
+import SystemAnnouncementsView from "@/pages/System/Announcements";
import SystemBackupsView from "@/pages/System/Backups";
import SystemLogsView from "@/pages/System/Logs";
import SystemProvidersView from "@/pages/System/Providers";
@@ -278,6 +279,12 @@ function useRoutes(): CustomRouteObject[] {
name: "Releases",
element: <SystemReleasesView></SystemReleasesView>,
},
+ {
+ path: "announcements",
+ name: "Announcements",
+ badge: data?.announcements,
+ element: <SystemAnnouncementsView></SystemAnnouncementsView>,
+ },
],
},
{
@@ -299,6 +306,7 @@ function useRoutes(): CustomRouteObject[] {
data?.providers,
data?.sonarr_signalr,
data?.radarr_signalr,
+ data?.announcements,
radarr,
sonarr,
]
diff --git a/frontend/src/apis/hooks/system.ts b/frontend/src/apis/hooks/system.ts
index 9be28c4d2..29e379a20 100644
--- a/frontend/src/apis/hooks/system.ts
+++ b/frontend/src/apis/hooks/system.ts
@@ -6,7 +6,15 @@ import { QueryKeys } from "../queries/keys";
import api from "../raw";
export function useBadges() {
- return useQuery([QueryKeys.System, QueryKeys.Badges], () => api.badges.all());
+ return useQuery(
+ [QueryKeys.System, QueryKeys.Badges],
+ () => api.badges.all(),
+ {
+ refetchOnWindowFocus: "always",
+ refetchInterval: 1000 * 60,
+ staleTime: 1000 * 10,
+ }
+ );
}
export function useFileSystem(
@@ -73,7 +81,7 @@ export function useSystemLogs() {
return useQuery([QueryKeys.System, QueryKeys.Logs], () => api.system.logs(), {
refetchOnWindowFocus: "always",
refetchInterval: 1000 * 60,
- staleTime: 1000,
+ staleTime: 1000 * 10,
});
}
@@ -90,6 +98,35 @@ export function useDeleteLogs() {
);
}
+export function useSystemAnnouncements() {
+ return useQuery(
+ [QueryKeys.System, QueryKeys.Announcements],
+ () => api.system.announcements(),
+ {
+ refetchOnWindowFocus: "always",
+ refetchInterval: 1000 * 60,
+ staleTime: 1000 * 10,
+ }
+ );
+}
+
+export function useSystemAnnouncementsAddDismiss() {
+ const client = useQueryClient();
+ return useMutation(
+ [QueryKeys.System, QueryKeys.Announcements],
+ (param: { hash: string }) => {
+ const { hash } = param;
+ return api.system.addAnnouncementsDismiss(hash);
+ },
+ {
+ onSuccess: (_, { hash }) => {
+ client.invalidateQueries([QueryKeys.System, QueryKeys.Announcements]);
+ client.invalidateQueries([QueryKeys.System, QueryKeys.Badges]);
+ },
+ }
+ );
+}
+
export function useSystemTasks() {
return useQuery(
[QueryKeys.System, QueryKeys.Tasks],
diff --git a/frontend/src/apis/queries/keys.ts b/frontend/src/apis/queries/keys.ts
index a3b6e94a7..45f30f12e 100644
--- a/frontend/src/apis/queries/keys.ts
+++ b/frontend/src/apis/queries/keys.ts
@@ -13,6 +13,7 @@ export enum QueryKeys {
Blacklist = "blacklist",
Search = "search",
Actions = "actions",
+ Announcements = "announcements",
Tasks = "tasks",
Backups = "backups",
Logs = "logs",
diff --git a/frontend/src/apis/raw/system.ts b/frontend/src/apis/raw/system.ts
index c2f0382ef..1b64d6b24 100644
--- a/frontend/src/apis/raw/system.ts
+++ b/frontend/src/apis/raw/system.ts
@@ -87,6 +87,19 @@ class SystemApi extends BaseApi {
await this.delete("/logs");
}
+ async announcements() {
+ const response = await this.get<DataWrapper<System.Announcements[]>>(
+ "/announcements"
+ );
+ return response.data;
+ }
+
+ async addAnnouncementsDismiss(hash: string) {
+ await this.post<DataWrapper<System.Announcements[]>>("/announcements", {
+ hash,
+ });
+ }
+
async tasks() {
const response = await this.get<DataWrapper<System.Task[]>>("/tasks");
return response.data;
diff --git a/frontend/src/pages/System/Announcements/index.tsx b/frontend/src/pages/System/Announcements/index.tsx
new file mode 100644
index 000000000..4e204431e
--- /dev/null
+++ b/frontend/src/pages/System/Announcements/index.tsx
@@ -0,0 +1,24 @@
+import { useSystemAnnouncements } from "@/apis/hooks";
+import { QueryOverlay } from "@/components/async";
+import { Container } from "@mantine/core";
+import { useDocumentTitle } from "@mantine/hooks";
+import { FunctionComponent } from "react";
+import Table from "./table";
+
+const SystemAnnouncementsView: FunctionComponent = () => {
+ const announcements = useSystemAnnouncements();
+
+ const { data } = announcements;
+
+ useDocumentTitle("Announcements - Bazarr (System)");
+
+ return (
+ <QueryOverlay result={announcements}>
+ <Container fluid px={0}>
+ <Table announcements={data ?? []}></Table>
+ </Container>
+ </QueryOverlay>
+ );
+};
+
+export default SystemAnnouncementsView;
diff --git a/frontend/src/pages/System/Announcements/table.tsx b/frontend/src/pages/System/Announcements/table.tsx
new file mode 100644
index 000000000..97f8cbe3e
--- /dev/null
+++ b/frontend/src/pages/System/Announcements/table.tsx
@@ -0,0 +1,91 @@
+import { useSystemAnnouncementsAddDismiss } from "@/apis/hooks";
+import { SimpleTable } from "@/components";
+import { MutateAction } from "@/components/async";
+import { useTableStyles } from "@/styles";
+import { faWindowClose } from "@fortawesome/free-solid-svg-icons";
+import { Anchor, Text } from "@mantine/core";
+import { FunctionComponent, useMemo } from "react";
+import { Column } from "react-table";
+
+interface Props {
+ announcements: readonly System.Announcements[];
+}
+
+const Table: FunctionComponent<Props> = ({ announcements }) => {
+ const columns: Column<System.Announcements>[] = useMemo<
+ Column<System.Announcements>[]
+ >(
+ () => [
+ {
+ Header: "Since",
+ accessor: "timestamp",
+ Cell: ({ value }) => {
+ const { classes } = useTableStyles();
+ return <Text className={classes.primary}>{value}</Text>;
+ },
+ },
+ {
+ Header: "Announcement",
+ accessor: "text",
+ Cell: ({ value }) => {
+ const { classes } = useTableStyles();
+ return <Text className={classes.primary}>{value}</Text>;
+ },
+ },
+ {
+ Header: "More info",
+ accessor: "link",
+ Cell: ({ value }) => {
+ if (value) {
+ return <Label link={value}>Link</Label>;
+ } else {
+ return <Text>n/a</Text>;
+ }
+ },
+ },
+ {
+ Header: "Dismiss",
+ accessor: "hash",
+ Cell: ({ row, value }) => {
+ const add = useSystemAnnouncementsAddDismiss();
+ return (
+ <MutateAction
+ label="Dismiss announcement"
+ disabled={!row.original.dismissible}
+ icon={faWindowClose}
+ mutation={add}
+ args={() => ({
+ hash: value,
+ })}
+ ></MutateAction>
+ );
+ },
+ },
+ ],
+ []
+ );
+
+ return (
+ <SimpleTable
+ columns={columns}
+ data={announcements}
+ tableStyles={{ emptyText: "No announcements for now, come back later!" }}
+ ></SimpleTable>
+ );
+};
+
+export default Table;
+
+interface LabelProps {
+ link: string;
+ children: string;
+}
+
+function Label(props: LabelProps): JSX.Element {
+ const { link, children } = props;
+ return (
+ <Anchor href={link} target="_blank" rel="noopener noreferrer">
+ {children}
+ </Anchor>
+ );
+}
diff --git a/frontend/src/pages/System/system.test.tsx b/frontend/src/pages/System/system.test.tsx
index f9c0b5dad..813654a7b 100644
--- a/frontend/src/pages/System/system.test.tsx
+++ b/frontend/src/pages/System/system.test.tsx
@@ -1,3 +1,4 @@
+import SystemAnnouncementsView from "@/pages/System/Announcements";
import { renderTest, RenderTestCase } from "@/tests/render";
import SystemBackupsView from "./Backups";
import SystemLogsView from "./Logs";
@@ -31,6 +32,10 @@ const cases: RenderTestCase[] = [
name: "tasks page",
ui: SystemTasksView,
},
+ {
+ name: "announcements page",
+ ui: SystemAnnouncementsView,
+ },
];
renderTest("System", cases);
diff --git a/frontend/src/types/api.d.ts b/frontend/src/types/api.d.ts
index ffd931d43..b19c682c0 100644
--- a/frontend/src/types/api.d.ts
+++ b/frontend/src/types/api.d.ts
@@ -5,6 +5,7 @@ interface Badge {
status: number;
sonarr_signalr: string;
radarr_signalr: string;
+ announcements: number;
}
declare namespace Language {
diff --git a/frontend/src/types/form.d.ts b/frontend/src/types/form.d.ts
index 99b16da88..6019a3fa0 100644
--- a/frontend/src/types/form.d.ts
+++ b/frontend/src/types/form.d.ts
@@ -74,4 +74,8 @@ declare namespace FormType {
subtitle: unknown;
original_format: PythonBoolean;
}
+
+ interface AddAnnouncementsDismiss {
+ hash: number;
+ }
}
diff --git a/frontend/src/types/system.d.ts b/frontend/src/types/system.d.ts
index dc4e33799..544d969ae 100644
--- a/frontend/src/types/system.d.ts
+++ b/frontend/src/types/system.d.ts
@@ -1,4 +1,12 @@
declare namespace System {
+ interface Announcements {
+ text: string;
+ link: string;
+ hash: string;
+ dismissible: boolean;
+ timestamp: string;
+ }
+
interface Task {
interval: string;
job_id: string;