add global settings context

will be useful going forward, and simplify widget props being passed around all over the place
This commit is contained in:
Ben Phelps 2022-09-21 09:00:57 +03:00
parent 244a76de0b
commit a677fbefbf
9 changed files with 57 additions and 28 deletions

View File

@ -1,10 +1,10 @@
import List from "components/bookmarks/list"; import List from "components/bookmarks/list";
export default function BookmarksGroup({ group, target }) { export default function BookmarksGroup({ group }) {
return ( return (
<div key={group.name} className="basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4 flex-1 p-1"> <div key={group.name} className="basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4 flex-1 p-1">
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{group.name}</h2> <h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{group.name}</h2>
<List bookmarks={group.bookmarks} target={target} /> <List bookmarks={group.bookmarks} />
</div> </div>
); );
} }

View File

@ -1,12 +1,17 @@
export default function Item({ bookmark, target = "_blank" }) { import { useContext } from "react";
import { SettingsContext } from "utils/settings-context";
export default function Item({ bookmark }) {
const { hostname } = new URL(bookmark.href); const { hostname } = new URL(bookmark.href);
const { settings } = useContext(SettingsContext);
return ( return (
<li key={bookmark.name}> <li key={bookmark.name}>
<a <a
href={bookmark.href} href={bookmark.href}
title={bookmark.name} title={bookmark.name}
target={target} target={settings.target ?? "_blank"}
className="block w-full text-left mb-3 cursor-pointer rounded-md font-medium text-theme-700 hover:text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/10 dark:bg-white/10 dark:hover:bg-white/20" className="block w-full text-left mb-3 cursor-pointer rounded-md font-medium text-theme-700 hover:text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/10 dark:bg-white/10 dark:hover:bg-white/20"
> >
<div className="flex"> <div className="flex">

View File

@ -1,10 +1,10 @@
import Item from "components/bookmarks/item"; import Item from "components/bookmarks/item";
export default function List({ bookmarks, target }) { export default function List({ bookmarks }) {
return ( return (
<ul className="mt-3 flex flex-col"> <ul className="mt-3 flex flex-col">
{bookmarks.map((bookmark) => ( {bookmarks.map((bookmark) => (
<Item key={bookmark.name} bookmark={bookmark} target={target} /> <Item key={bookmark.name} bookmark={bookmark} />
))} ))}
</ul> </ul>
); );

View File

@ -2,7 +2,7 @@ import classNames from "classnames";
import List from "components/services/list"; import List from "components/services/list";
export default function ServicesGroup({ services, target, layout }) { export default function ServicesGroup({ services, layout }) {
return ( return (
<div <div
key={services.name} key={services.name}
@ -12,7 +12,7 @@ export default function ServicesGroup({ services, target, layout }) {
)} )}
> >
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{services.name}</h2> <h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{services.name}</h2>
<List services={services.services} target={target} layout={layout} /> <List services={services.services} layout={layout} />
</div> </div>
); );
} }

View File

@ -1,10 +1,13 @@
import Image from "next/future/image"; import Image from "next/future/image";
import { useContext } from "react";
import { Disclosure } from "@headlessui/react"; import { Disclosure } from "@headlessui/react";
import Status from "./status"; import Status from "./status";
import Widget from "./widget"; import Widget from "./widget";
import Docker from "./widgets/service/docker"; import Docker from "./widgets/service/docker";
import { SettingsContext } from "utils/settings-context";
function resolveIcon(icon) { function resolveIcon(icon) {
if (icon.startsWith("http")) { if (icon.startsWith("http")) {
return `/api/proxy?url=${encodeURIComponent(icon)}`; return `/api/proxy?url=${encodeURIComponent(icon)}`;
@ -21,8 +24,9 @@ function resolveIcon(icon) {
return `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${icon}.png`; return `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${icon}.png`;
} }
export default function Item({ service, target = "_blank" }) { export default function Item({ service }) {
const hasLink = service.href && service.href !== "#"; const hasLink = service.href && service.href !== "#";
const { settings } = useContext(SettingsContext);
return ( return (
<li key={service.name}> <li key={service.name}>
@ -37,7 +41,7 @@ export default function Item({ service, target = "_blank" }) {
(hasLink ? ( (hasLink ? (
<a <a
href={service.href} href={service.href}
target={target} target={settings.target ?? "_blank"}
rel="noreferrer" rel="noreferrer"
className="flex-shrink-0 flex items-center justify-center w-12 " className="flex-shrink-0 flex items-center justify-center w-12 "
> >
@ -52,7 +56,7 @@ export default function Item({ service, target = "_blank" }) {
{hasLink ? ( {hasLink ? (
<a <a
href={service.href} href={service.href}
target={target} target={settings.target ?? "_blank"}
rel="noreferrer" rel="noreferrer"
className="flex-1 flex items-center justify-between rounded-r-md " className="flex-1 flex items-center justify-between rounded-r-md "
> >

View File

@ -14,7 +14,7 @@ const columnMap = [
"grid-cols-1 md:grid-cols-2 lg:grid-cols-8", "grid-cols-1 md:grid-cols-2 lg:grid-cols-8",
]; ];
export default function List({ services, target, layout }) { export default function List({ services, layout }) {
return ( return (
<ul <ul
className={classNames( className={classNames(
@ -23,7 +23,7 @@ export default function List({ services, target, layout }) {
)} )}
> >
{services.map((service) => ( {services.map((service) => (
<Item key={service.name} target={target} service={service} /> <Item key={service.name} service={service} />
))} ))}
</ul> </ul>
); );

View File

@ -8,6 +8,7 @@ import "styles/theme.css";
import "utils/i18n"; import "utils/i18n";
import { ColorProvider } from "utils/color-context"; import { ColorProvider } from "utils/color-context";
import { ThemeProvider } from "utils/theme-context"; import { ThemeProvider } from "utils/theme-context";
import { SettingsProvider } from "utils/settings-context";
function MyApp({ Component, pageProps }) { function MyApp({ Component, pageProps }) {
return ( return (
@ -18,7 +19,9 @@ function MyApp({ Component, pageProps }) {
> >
<ColorProvider> <ColorProvider>
<ThemeProvider> <ThemeProvider>
<Component {...pageProps} /> <SettingsProvider>
<Component {...pageProps} />
</SettingsProvider>
</ThemeProvider> </ThemeProvider>
</ColorProvider> </ColorProvider>
</SWRConfig> </SWRConfig>

View File

@ -13,6 +13,7 @@ import Revalidate from "components/revalidate";
import { getSettings } from "utils/config"; import { getSettings } from "utils/config";
import { ColorContext } from "utils/color-context"; import { ColorContext } from "utils/color-context";
import { ThemeContext } from "utils/theme-context"; import { ThemeContext } from "utils/theme-context";
import { SettingsContext } from "utils/settings-context";
const ThemeToggle = dynamic(() => import("components/theme-toggle"), { const ThemeToggle = dynamic(() => import("components/theme-toggle"), {
ssr: false, ssr: false,
@ -26,22 +27,23 @@ const rightAlignedWidgets = ["weatherapi", "openweathermap", "weather", "search"
export function getStaticProps() { export function getStaticProps() {
try { try {
const settings = getSettings(); const { providers, ...settings } = getSettings();
return { return {
props: { props: {
settings, initialSettings: settings,
}, },
}; };
} catch (e) { } catch (e) {
return { return {
props: { props: {
settings: {}, initialSettings: {},
}, },
}; };
} }
} }
export default function Index({ settings }) { export default function Index({ initialSettings }) {
const { data: errorsData } = useSWR("/api/validate"); const { data: errorsData } = useSWR("/api/validate");
if (errorsData && errorsData.length > 0) { if (errorsData && errorsData.length > 0) {
@ -68,20 +70,25 @@ export default function Index({ settings }) {
); );
} }
return <Home settings={settings} />; return <Home initialSettings={initialSettings} />;
} }
function Home({ settings }) { function Home({ initialSettings }) {
const { i18n } = useTranslation(); const { i18n } = useTranslation();
const { theme, setTheme } = useContext(ThemeContext); const { theme, setTheme } = useContext(ThemeContext);
const { color, setColor } = useContext(ColorContext); const { color, setColor } = useContext(ColorContext);
const { settings, setSettings } = useContext(SettingsContext);
if (initialSettings) {
setSettings(initialSettings);
}
const { data: services } = useSWR("/api/services"); const { data: services } = useSWR("/api/services");
const { data: bookmarks } = useSWR("/api/bookmarks"); const { data: bookmarks } = useSWR("/api/bookmarks");
const { data: widgets } = useSWR("/api/widgets"); const { data: widgets } = useSWR("/api/widgets");
const wrappedStyle = {}; const wrappedStyle = {};
if (settings.background) { if (settings && settings.background) {
wrappedStyle.backgroundImage = `url(${settings.background})`; wrappedStyle.backgroundImage = `url(${settings.background})`;
wrappedStyle.backgroundSize = "cover"; wrappedStyle.backgroundSize = "cover";
wrappedStyle.opacity = settings.backgroundOpacity ?? 1; wrappedStyle.opacity = settings.backgroundOpacity ?? 1;
@ -133,12 +140,7 @@ function Home({ settings }) {
{services && ( {services && (
<div className="flex flex-wrap p-8 items-start"> <div className="flex flex-wrap p-8 items-start">
{services.map((group) => ( {services.map((group) => (
<ServicesGroup <ServicesGroup key={group.name} services={group} layout={settings.layout?.[group.name]} />
key={group.name}
services={group}
target={settings?.target}
layout={settings.layout?.[group.name]}
/>
))} ))}
</div> </div>
)} )}
@ -146,7 +148,7 @@ function Home({ settings }) {
{bookmarks && ( {bookmarks && (
<div className="grow flex flex-wrap pt-0 p-8"> <div className="grow flex flex-wrap pt-0 p-8">
{bookmarks.map((group) => ( {bookmarks.map((group) => (
<BookmarksGroup key={group.name} group={group} target={settings?.target} /> <BookmarksGroup key={group.name} group={group} />
))} ))}
</div> </div>
)} )}

View File

@ -0,0 +1,15 @@
import { createContext, useState, useMemo } from "react";
export const SettingsContext = createContext();
export function SettingsProvider({ initialSettings, children }) {
const [settings, setSettings] = useState({});
if (initialSettings) {
setSettings(initialSettings);
}
const value = useMemo(() => ({ settings, setSettings }), [settings]);
return <SettingsContext.Provider value={value}>{children}</SettingsContext.Provider>;
}