From 1f9359988b4a0e2d48c54768276258baf77bceb0 Mon Sep 17 00:00:00 2001 From: Beebles <102569435+beebls@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:57:21 -0600 Subject: feat(motd): add motd component (untested) --- frontend/src/components/MotdDisplay.tsx | 112 ++++++++++++++++++ frontend/src/components/PluginView.tsx | 2 + frontend/src/store.ts | 197 ++++++++++++++++++++++++++++++++ frontend/src/store.tsx | 149 ------------------------ 4 files changed, 311 insertions(+), 149 deletions(-) create mode 100644 frontend/src/components/MotdDisplay.tsx create mode 100644 frontend/src/store.ts delete mode 100644 frontend/src/store.tsx 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(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 ( + + +
+ {motd?.name} + + + +
+ {motd?.description} +
+
+ ) + +} \ No newline at end of file diff --git a/frontend/src/components/PluginView.tsx b/frontend/src/components/PluginView.tsx index 19afbca5..682abfc1 100644 --- a/frontend/src/components/PluginView.tsx +++ b/frontend/src/components/PluginView.tsx @@ -8,6 +8,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 { hiddenPlugins } = useDeckyState(); @@ -37,6 +38,7 @@ const PluginView: FC = () => { return ( <> +
; + +export async function getStore(): Promise { + return await getSetting('store', Store.Default); +} + +export async function getMotd(): Promise { + let version = await window.DeckyPluginLoader.updateVersion(); + let store = await getSetting('store', null); + let customURL = await getSetting('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, +): Promise { + let version = await window.DeckyPluginLoader.updateVersion(); + let store = await getSetting('store', null); + let customURL = await getSetting('store-url', 'https://plugins.deckbrew.xyz/plugins'); + + let query: URLSearchParams | string = new URLSearchParams(); + sort_by && query.set('sort_by', sort_by); + sort_direction && query.set('sort_direction', sort_direction); + query = '?' + String(query); + + let storeURL; + if (store === null) { + console.log('Could not get store, using Default.'); + await setSetting('store', Store.Default); + store = Store.Default; + } + switch (+store) { + case Store.Default: + storeURL = 'https://plugins.deckbrew.xyz/plugins'; + break; + case Store.Testing: + storeURL = 'https://testing.deckbrew.xyz/plugins'; + break; + case Store.Custom: + storeURL = customURL; + break; + default: + console.error('Somehow you ended up without a standard URL, using the default URL.'); + storeURL = 'https://plugins.deckbrew.xyz/plugins'; + break; + return fetch(storeURL, { + method: 'GET', + headers: { + 'X-Decky-Version': version.current, + }, + }).then((r) => r.json()); + } + switch (+store) { + case Store.Default: + storeURL = 'https://plugins.deckbrew.xyz/plugins'; + break; + case Store.Testing: + storeURL = 'https://testing.deckbrew.xyz/plugins'; + break; + case Store.Custom: + storeURL = customURL; + break; + default: + console.error('Somehow you ended up without a standard URL, using the default URL.'); + storeURL = 'https://plugins.deckbrew.xyz/plugins'; + break; + } + return fetch(storeURL + query, { + method: 'GET', + headers: { + 'X-Decky-Version': version.current, + }, + }).then((r) => r.json()); +} + +export async function installFromURL(url: string) { + const splitURL = url.split('/'); + await installPlugin(url, splitURL[splitURL.length - 1].replace('.zip', '')); +} + +export async function requestPluginInstall(plugin: string, selectedVer: StorePluginVersion, installType: InstallType) { + const artifactUrl = selectedVer.artifact ?? pluginUrl(selectedVer.hash); + await installPlugin(artifactUrl, plugin, selectedVer.name, selectedVer.hash, installType); +} + +export async function requestMultiplePluginInstalls(requests: PluginInstallRequest[]) { + await installPlugins( + requests.map(({ plugin, installType, selectedVer }) => ({ + name: plugin, + artifact: selectedVer.artifact ?? pluginUrl(selectedVer.hash), + version: selectedVer.name, + hash: selectedVer.hash, + install_type: installType, + })), + ); +} + +export async function checkForPluginUpdates(plugins: Plugin[]): Promise { + const serverData = await getPluginList(); + const updateMap = new Map(); + for (let plugin of plugins) { + const remotePlugin = serverData?.find((x) => x.name == plugin.name); + if (remotePlugin && remotePlugin.versions?.length > 0 && plugin.version != remotePlugin?.versions?.[0]?.name) { + updateMap.set(plugin.name, remotePlugin.versions[0]); + } + } + return updateMap; +} + +function pluginUrl(hash: string) { + return `https://cdn.tzatzikiweeb.moe/file/steam-deck-homebrew/versions/${hash}.zip`; +} diff --git a/frontend/src/store.tsx b/frontend/src/store.tsx deleted file mode 100644 index 8ab8f50a..00000000 --- a/frontend/src/store.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { InstallType, Plugin, installPlugin, installPlugins } from './plugin'; -import { getSetting, setSetting } from './utils/settings'; - -export enum Store { - Default, - Testing, - Custom, -} - -export enum SortOptions { - name = 'name', - date = 'date', - downloads = 'downloads', -} - -export enum SortDirections { - ascending = 'asc', - descending = 'desc', -} - -export interface StorePluginVersion { - name: string; - hash: string; - artifact: string | undefined | null; -} - -export interface StorePlugin { - id: number; - name: string; - versions: StorePluginVersion[]; - author: string; - description: string; - tags: string[]; - image_url: string; -} - -export interface PluginInstallRequest { - plugin: string; - selectedVer: StorePluginVersion; - installType: InstallType; -} - -// name: version -export type PluginUpdateMapping = Map; - -export async function getStore(): Promise { - return await getSetting('store', Store.Default); -} - -export async function getPluginList( - sort_by: SortOptions | null = null, - sort_direction: SortDirections | null = null, -): Promise { - let version = await window.DeckyPluginLoader.updateVersion(); - let store = await getSetting('store', null); - let customURL = await getSetting('store-url', 'https://plugins.deckbrew.xyz/plugins'); - - let query: URLSearchParams | string = new URLSearchParams(); - sort_by && query.set('sort_by', sort_by); - sort_direction && query.set('sort_direction', sort_direction); - query = '?' + String(query); - - let storeURL; - if (store === null) { - console.log('Could not get store, using Default.'); - await setSetting('store', Store.Default); - store = Store.Default; - } - switch (+store) { - case Store.Default: - storeURL = 'https://plugins.deckbrew.xyz/plugins'; - break; - case Store.Testing: - storeURL = 'https://testing.deckbrew.xyz/plugins'; - break; - case Store.Custom: - storeURL = customURL; - break; - default: - console.error('Somehow you ended up without a standard URL, using the default URL.'); - storeURL = 'https://plugins.deckbrew.xyz/plugins'; - break; - return fetch(storeURL, { - method: 'GET', - headers: { - 'X-Decky-Version': version.current, - }, - }).then((r) => r.json()); - } - switch (+store) { - case Store.Default: - storeURL = 'https://plugins.deckbrew.xyz/plugins'; - break; - case Store.Testing: - storeURL = 'https://testing.deckbrew.xyz/plugins'; - break; - case Store.Custom: - storeURL = customURL; - break; - default: - console.error('Somehow you ended up without a standard URL, using the default URL.'); - storeURL = 'https://plugins.deckbrew.xyz/plugins'; - break; - } - return fetch(storeURL + query, { - method: 'GET', - headers: { - 'X-Decky-Version': version.current, - }, - }).then((r) => r.json()); -} - -export async function installFromURL(url: string) { - const splitURL = url.split('/'); - await installPlugin(url, splitURL[splitURL.length - 1].replace('.zip', '')); -} - -export async function requestPluginInstall(plugin: string, selectedVer: StorePluginVersion, installType: InstallType) { - const artifactUrl = selectedVer.artifact ?? pluginUrl(selectedVer.hash); - await installPlugin(artifactUrl, plugin, selectedVer.name, selectedVer.hash, installType); -} - -export async function requestMultiplePluginInstalls(requests: PluginInstallRequest[]) { - await installPlugins( - requests.map(({ plugin, installType, selectedVer }) => ({ - name: plugin, - artifact: selectedVer.artifact ?? pluginUrl(selectedVer.hash), - version: selectedVer.name, - hash: selectedVer.hash, - install_type: installType, - })), - ); -} - -export async function checkForPluginUpdates(plugins: Plugin[]): Promise { - const serverData = await getPluginList(); - const updateMap = new Map(); - for (let plugin of plugins) { - const remotePlugin = serverData?.find((x) => x.name == plugin.name); - if (remotePlugin && remotePlugin.versions?.length > 0 && plugin.version != remotePlugin?.versions?.[0]?.name) { - updateMap.set(plugin.name, remotePlugin.versions[0]); - } - } - return updateMap; -} - -function pluginUrl(hash: string) { - return `https://cdn.tzatzikiweeb.moe/file/steam-deck-homebrew/versions/${hash}.zip`; -} -- cgit v1.2.3