Move Settings context data into Redux settings slice

Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
Julius Volz 2024-07-15 22:19:47 +02:00
parent edf31da311
commit 8fae131733
11 changed files with 48 additions and 60 deletions

View file

@ -55,13 +55,12 @@ import TSDBStatusPage from "./pages/TSDBStatusPage";
import FlagsPage from "./pages/FlagsPage";
import ConfigPage from "./pages/ConfigPage";
import AgentPage from "./pages/AgentPage";
import { Suspense, useContext } from "react";
import { Suspense } from "react";
import ErrorBoundary from "./components/ErrorBoundary";
import { ThemeSelector } from "./components/ThemeSelector";
import { SettingsContext } from "./settings";
import { Notifications } from "@mantine/notifications";
import { useAppDispatch } from "./state/hooks";
import { updateSettings } from "./state/settingsSlice";
import { updateSettings, useSettings } from "./state/settingsSlice";
import SettingsMenu from "./components/SettingsMenu";
const queryClient = new QueryClient();
@ -182,12 +181,13 @@ const navLinkXPadding = "md";
function App() {
const [scroll, scrollTo] = useWindowScroll();
const [opened, { toggle }] = useDisclosure();
const { agentMode } = useContext(SettingsContext);
const pathPrefix = getPathPrefix(window.location.pathname);
const dispatch = useAppDispatch();
dispatch(updateSettings({ pathPrefix }));
const { agentMode } = useSettings();
const navLinks = (
<>
{mainNavPages

View file

@ -1,5 +1,5 @@
import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
import { useAppSelector } from "../state/hooks";
import { useSettings } from "../state/settingsSlice";
export const API_PATH = "api/v1";
@ -92,7 +92,7 @@ export const useAPIQuery = <T>({
params,
enabled,
}: QueryOptions) => {
const pathPrefix = useAppSelector((state) => state.settings.pathPrefix);
const { pathPrefix } = useSettings();
return useQuery<SuccessAPIResponse<T>>({
queryKey: key ? [key] : [path, params],
@ -105,7 +105,7 @@ export const useAPIQuery = <T>({
};
export const useSuspenseAPIQuery = <T>({ key, path, params }: QueryOptions) => {
const pathPrefix = useAppSelector((state) => state.settings.pathPrefix);
const { pathPrefix } = useSettings();
return useSuspenseQuery<SuccessAPIResponse<T>>({
queryKey: key ? [key] : [path, params],

View file

@ -1,8 +1,8 @@
import { Popover, ActionIcon, Fieldset, Checkbox, Stack } from "@mantine/core";
import { IconSettings } from "@tabler/icons-react";
import { FC } from "react";
import { useAppDispatch, useAppSelector } from "../state/hooks";
import { updateSettings } from "../state/settingsSlice";
import { useAppDispatch } from "../state/hooks";
import { updateSettings, useSettings } from "../state/settingsSlice";
const SettingsMenu: FC = () => {
const {
@ -12,7 +12,7 @@ const SettingsMenu: FC = () => {
enableSyntaxHighlighting,
enableLinter,
showAnnotations,
} = useAppSelector((state) => state.settings);
} = useSettings();
const dispatch = useAppDispatch();
return (

View file

@ -1,33 +1,14 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import { Settings, SettingsContext } from "./settings.ts";
import store from "./state/store.ts";
import { Provider } from "react-redux";
import "./fonts/codicon.ttf";
// Declared/defined in public/index.html, value replaced by Prometheus when serving bundle.
declare const GLOBAL_CONSOLES_LINK: string;
declare const GLOBAL_AGENT_MODE: string;
declare const GLOBAL_READY: string;
const settings: Settings = {
consolesLink:
GLOBAL_CONSOLES_LINK === "CONSOLES_LINK_PLACEHOLDER" ||
GLOBAL_CONSOLES_LINK === "" ||
GLOBAL_CONSOLES_LINK === null
? null
: GLOBAL_CONSOLES_LINK,
agentMode: GLOBAL_AGENT_MODE === "true",
ready: GLOBAL_READY === "true",
};
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<SettingsContext.Provider value={settings}>
<Provider store={store}>
<App />
</Provider>
</SettingsContext.Provider>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);

View file

@ -22,6 +22,7 @@ import { useAppDispatch, useAppSelector } from "../state/hooks";
import { IconInfoCircle, IconSearch } from "@tabler/icons-react";
import { LabelBadges } from "../components/LabelBadges";
import { updateAlertFilters } from "../state/alertsPageSlice";
import { useSettings } from "../state/settingsSlice";
export default function AlertsPage() {
const { data } = useSuspenseAPIQuery<AlertingRulesResult>({
@ -32,9 +33,7 @@ export default function AlertsPage() {
});
const dispatch = useAppDispatch();
const showAnnotations = useAppSelector(
(state) => state.settings.showAnnotations
);
const { showAnnotations } = useSettings();
const filters = useAppSelector((state) => state.alertsPage.filters);
const ruleStatsCount = {
@ -56,8 +55,8 @@ export default function AlertsPage() {
o === "inactive"
? badgeClasses.healthOk
: o === "pending"
? badgeClasses.healthWarn
: badgeClasses.healthErr
? badgeClasses.healthWarn
: badgeClasses.healthErr
}
placeholder="Filter by alert state"
values={filters.state}
@ -121,8 +120,8 @@ export default function AlertsPage() {
numFiring > 0
? "5px solid var(--mantine-color-red-4)"
: numPending > 0
? "5px solid var(--mantine-color-orange-5)"
: "5px solid var(--mantine-color-green-4)",
? "5px solid var(--mantine-color-orange-5)"
: "5px solid var(--mantine-color-green-4)",
}}
>
<Accordion.Control>

View file

@ -1,8 +1,8 @@
import { Stack, Card, Group, Table, Text } from "@mantine/core";
import { useSuspenseAPIQuery } from "../api/api";
import { TSDBStatusResult } from "../api/responseTypes/tsdbStatus";
import { useAppSelector } from "../state/hooks";
import { formatTimestamp } from "../lib/formatTime";
import { useSettings } from "../state/settingsSlice";
export default function TSDBStatusPage() {
const {
@ -17,7 +17,7 @@ export default function TSDBStatusPage() {
},
} = useSuspenseAPIQuery<TSDBStatusResult>({ path: `/status/tsdb` });
const useLocalTime = useAppSelector((state) => state.settings.useLocalTime);
const { useLocalTime } = useSettings();
const unixToTime = (unix: number): string => {
const formatted = formatTimestamp(unix, useLocalTime);

View file

@ -21,11 +21,11 @@ import { useAPIQuery } from "../../api/api";
import classes from "./DataTable.module.css";
import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone";
import { useAppSelector } from "../../state/hooks";
import { formatTimestamp } from "../../lib/formatTime";
import HistogramChart from "./HistogramChart";
import { Histogram } from "../../types/types";
import { bucketRangeString } from "./HistogramHelpers";
import { useSettings } from "../../state/settingsSlice";
dayjs.extend(timezone);
const maxFormattableSeries = 1000;
@ -64,7 +64,7 @@ const DataTable: FC<DataTableProps> = ({ expr, evalTime, retriggerIdx }) => {
expr !== "" && refetch();
}, [retriggerIdx, refetch, expr, evalTime]);
const useLocalTime = useAppSelector((state) => state.settings.useLocalTime);
const { useLocalTime } = useSettings();
// Show a skeleton only on the first load, not on subsequent ones.
if (isLoading) {

View file

@ -61,7 +61,7 @@ import {
} from "@tabler/icons-react";
import { useAPIQuery } from "../../api/api";
import { notifications } from "@mantine/notifications";
import { useAppSelector } from "../../state/hooks";
import { useSettings } from "../../state/settingsSlice";
const promqlExtension = new PromQLExtension();
@ -126,7 +126,7 @@ const ExpressionInput: FC<ExpressionInputProps> = ({
enableAutocomplete,
enableSyntaxHighlighting,
enableLinter,
} = useAppSelector((state) => state.settings);
} = useSettings();
const [expr, setExpr] = useState(initialExpr);
useEffect(() => {
setExpr(initialExpr);

View file

@ -2,7 +2,7 @@ import { Group, ActionIcon, CloseButton } from "@mantine/core";
import { DatesProvider, DateTimePicker } from "@mantine/dates";
import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
import { FC } from "react";
import { useAppSelector } from "../../state/hooks";
import { useSettings } from "../../state/settingsSlice";
interface TimeInputProps {
time: number | null; // Timestamp in milliseconds.
@ -20,7 +20,7 @@ const TimeInput: FC<TimeInputProps> = ({
onChangeTime,
}) => {
const baseTime = () => (time !== null ? time : Date.now().valueOf());
const useLocalTime = useAppSelector((state) => state.settings.useLocalTime);
const { useLocalTime } = useSettings();
return (
<Group gap={5}>

View file

@ -1,13 +0,0 @@
import { createContext } from "react";
export interface Settings {
consolesLink: string | null;
agentMode: boolean;
ready: boolean;
}
export const SettingsContext = createContext<Settings>({
consolesLink: null,
agentMode: false,
ready: false,
});

View file

@ -1,6 +1,10 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { useAppSelector } from "./hooks";
interface Settings {
consolesLink: string | null;
agentMode: boolean;
ready: boolean;
pathPrefix: string;
useLocalTime: boolean;
enableQueryHistory: boolean;
@ -10,7 +14,20 @@ interface Settings {
showAnnotations: boolean;
}
// Declared/defined in public/index.html, value replaced by Prometheus when serving bundle.
declare const GLOBAL_CONSOLES_LINK: string;
declare const GLOBAL_AGENT_MODE: string;
declare const GLOBAL_READY: string;
const initialState: Settings = {
consolesLink:
GLOBAL_CONSOLES_LINK === "CONSOLES_LINK_PLACEHOLDER" ||
GLOBAL_CONSOLES_LINK === "" ||
GLOBAL_CONSOLES_LINK === null
? null
: GLOBAL_CONSOLES_LINK,
agentMode: GLOBAL_AGENT_MODE === "true",
ready: GLOBAL_READY === "true",
pathPrefix: "",
useLocalTime: false,
enableQueryHistory: false,
@ -32,4 +49,8 @@ export const settingsSlice = createSlice({
export const { updateSettings } = settingsSlice.actions;
export const useSettings = () => {
return useAppSelector((state) => state.settings);
};
export default settingsSlice.reducer;