diff options
| author | Beebles <102569435+beebls@users.noreply.github.com> | 2024-08-07 16:57:21 -0600 |
|---|---|---|
| committer | Beebles <102569435+beebls@users.noreply.github.com> | 2025-08-01 06:45:40 -0600 |
| commit | 5a212e95fc2dfb6725b6996b4a72a2acd8a6b942 (patch) | |
| tree | f79e850d9cb8f3858c882db48c243093862dce13 | |
| parent | 8f41eb93ef80bfbf3851ce8a82ea0f88c87e6c68 (diff) | |
| download | decky-loader-5a212e95fc2dfb6725b6996b4a72a2acd8a6b942.tar.gz decky-loader-5a212e95fc2dfb6725b6996b4a72a2acd8a6b942.zip | |
feat(motd): add motd component (untested)
| -rw-r--r-- | frontend/src/components/MotdDisplay.tsx | 112 | ||||
| -rw-r--r-- | frontend/src/components/PluginView.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/store.ts (renamed from frontend/src/store.tsx) | 48 |
3 files changed, 162 insertions, 0 deletions
diff --git a/frontend/src/components/MotdDisplay.tsx b/frontend/src/components/MotdDisplay.tsx new file mode 100644 index 00000000..3c651b11 --- /dev/null +++ b/frontend/src/components/MotdDisplay.tsx @@ -0,0 +1,112 @@ +import {useEffect, useMemo, useState} from 'react' +import { getMotd, Motd } from '../store'; +import { useSetting } from '../utils/hooks/useSetting'; +import { DialogButton, Focusable, PanelSection } from '@decky/ui'; +import { FaTimes } from 'react-icons/fa'; + +const SEVERITIES = { + High: { + color: "#bb1414", + text: "#fff", + }, + Medium: { + color: "#bbbb14", + text: "#fff", + }, + Low: { + color: "#1488bb", + text: "#fff", + }, +}; + +const welcomeMotd: Motd = { + id: "welcomeMotd", + name: "Welcome to Decky!", + date: Date.now().toString(), + description: "We hope you enjoy using Decky! If you have any questions or feedback, please let us know.", + severity: "Low", +} + +export function MotdDisplay() { + const [motd, setMotd] = useState<Motd | null>(null); + // showWelcome will display a welcome motd, the welcome motd has an id of "welcome" and once that is saved to hiddenMotdId, it will not show again + const [hiddenMotdId, setHiddenMotdId] = useSetting("hiddenMotdId", "showWelcome") + + async function fetchMotd() { + const motd = await getMotd(); + setMotd(motd); + } + + useEffect(() => { + void fetchMotd(); + }, []) + + useEffect(() => { + if (hiddenMotdId === 'showWelcome') { + setMotd(welcomeMotd); + } + }, [hiddenMotdId]) + + function hideMotd() { + if (motd) { + setHiddenMotdId(motd.id); + void fetchMotd(); + } + } + + const hidden = useMemo(() => { + return hiddenMotdId === motd?.id; + }, [hiddenMotdId, motd]); + + if (!motd || !motd?.name || hidden) { + return null; + } + + const severity = SEVERITIES[motd?.severity || "Low"]; + + return ( + <PanelSection> + <Focusable + style={{ + // Transparency is 20% of the color + backgroundColor: `${severity.color}33`, + color: severity.text, + borderColor: severity.color, + borderWidth: "2px", + borderStyle: "solid", + padding: "0.7rem", + display: "flex", + flexDirection: "column", + position: "relative", + }} + > + <div style={{ display: "flex", justifyContent: "space-between" }}> + <span style={{ fontWeight: "bold" }}>{motd?.name}</span> + <DialogButton + style={{ + width: "1rem", + minWidth: "1rem", + height: "1rem", + padding: "0", + display: "flex", + alignItems: "center", + justifyContent: "center", + position: "absolute", + top: ".75rem", + right: ".75rem", + }} + onClick={hideMotd} + > + <FaTimes + style={{ + height: ".75rem", + }} + /> + </DialogButton> + </div> + <span style={{ fontSize: "0.75rem", whiteSpace: "pre-line" }}>{motd?.description}</span> + </Focusable> + </PanelSection> + ) + +}
\ No newline at end of file diff --git a/frontend/src/components/PluginView.tsx b/frontend/src/components/PluginView.tsx index 1d39972e..3f72486f 100644 --- a/frontend/src/components/PluginView.tsx +++ b/frontend/src/components/PluginView.tsx @@ -7,6 +7,7 @@ import { useDeckyState } from './DeckyState'; import NotificationBadge from './NotificationBadge'; import { useQuickAccessVisible } from './QuickAccessVisibleState'; import TitleView from './TitleView'; +import { MotdDisplay } from './MotdDisplay'; const PluginView: FC = () => { const { plugins, hiddenPlugins, updates, activePlugin, pluginOrder, setActivePlugin, closeActivePlugin } = @@ -36,6 +37,7 @@ const PluginView: FC = () => { return ( <> <TitleView /> + <MotdDisplay /> <div style={{ paddingTop: '16px', diff --git a/frontend/src/store.tsx b/frontend/src/store.ts index dfd9b04b..f3d3addd 100644 --- a/frontend/src/store.tsx +++ b/frontend/src/store.ts @@ -42,6 +42,14 @@ export interface PluginInstallRequest { installType: InstallType; } +export interface Motd { + id: string; + name: string; + description: string; + date: string; + severity: "High" | "Medium" | "Low"; +} + // name: version export type PluginUpdateMapping = Map<string, StorePluginVersion>; @@ -49,6 +57,46 @@ export async function getStore(): Promise<Store> { return await getSetting<Store>('store', Store.Default); } +export async function getMotd(): Promise<Motd> { + let version = await window.DeckyPluginLoader.updateVersion(); + let store = await getSetting<Store | null>('store', null); + let customURL = await getSetting<string>('motd-url', 'https://plugins.deckbrew.xyz/v1/motd'); + + if (store === null) { + console.log('Could not get store, using Default.'); + await setSetting('store', Store.Default); + store = Store.Default; + } + + let resolvedURL; + switch (store) { + case Store.Default: + resolvedURL = 'https://plugins.deckbrew.xyz/v1/motd'; + break; + case Store.Testing: + resolvedURL = 'https://testing.deckbrew.xyz/v1/motd'; + break; + case Store.Custom: + resolvedURL = customURL; + break; + default: + console.error('Somehow you ended up without a standard URL, using the default URL.'); + resolvedURL = 'https://plugins.deckbrew.xyz/v1/motd'; + break; + } + return fetch(resolvedURL, { + method: 'GET', + headers: { + 'X-Decky-Version': version.current, + }, + }).then((r) => { + if (r.status === 200) { + return r.json(); + } + return null; + }); +} + export async function getPluginList( sort_by: SortOptions | null = null, sort_direction: SortDirections | null = null, |
