diff --git a/backend/Cargo.lock b/backend/Cargo.lock index dd974217..9e7e5ea2 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -2645,7 +2645,7 @@ dependencies = [ [[package]] name = "rai-pal" -version = "0.1.24" +version = "0.1.25" dependencies = [ "async-trait", "byteorder", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index bbb5392f..0298d12e 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rai-pal" -version = "0.1.24" +version = "0.1.25" authors = ["Raicuparta"] license = "GPL-3.0-or-later" repository = "https://github.com/Raicuparta/rai-pal" @@ -24,7 +24,7 @@ specta = "1.0.5" tauri-specta = { version = "1.0.2", features = ["typescript"] } serde = { version = "1.0", features = ["derive"] } byteorder = "1.5.0" -tauri = { version = "1.5.2", features = [ "dialog-open", "updater", "shell-open", "dialog"] } +tauri = { version = "1.5.2", features = [ "dialog-confirm", "dialog-open", "updater", "shell-open", "dialog"] } reqwest = "0.11.22" goblin = "0.7.1" open = "5.0.0" diff --git a/backend/src/events.rs b/backend/src/events.rs index 4eb6dac7..efb492e7 100644 --- a/backend/src/events.rs +++ b/backend/src/events.rs @@ -1,32 +1,25 @@ use serde::Serialize; use tauri::Manager; -use crate::{ - result::Result, - serializable_enum, -}; +use crate::serializable_enum; serializable_enum!(AppEvent { SyncInstalledGames, SyncOwnedGames, - SyncDiscoverGames, SyncMods, ExecutedSteamCommand, GameAdded, GameRemoved, + Error, }); pub trait EventEmitter { - fn emit_event(&self, event: AppEvent, payload: TPayload) - -> Result; + fn emit_event(&self, event: AppEvent, payload: TPayload); } impl EventEmitter for tauri::AppHandle { - fn emit_event( - &self, - event: AppEvent, - payload: TPayload, - ) -> Result { - Ok(self.emit_all(&event.to_string(), payload)?) + fn emit_event(&self, event: AppEvent, payload: TPayload) { + self.emit_all(&event.to_string(), payload) + .unwrap_or_else(|err| eprintln!("Failed to emit event: {err}")); } } diff --git a/backend/src/main.rs b/backend/src/main.rs index e74da477..f0e10bcb 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -33,10 +33,6 @@ use result::{ Error, Result, }; -use steam::{ - appinfo, - id_lists::SteamGame, -}; use steamlocate::SteamDir; use tauri::{ api::dialog::message, @@ -61,7 +57,6 @@ mod windows; struct AppState { installed_games: Mutex>, owned_games: Mutex>>, - discover_games: Mutex>>, mod_loaders: Mutex>, } @@ -106,12 +101,6 @@ async fn get_owned_games(state: tauri::State<'_, AppState>) -> Result) -> Result> { - get_state_data(&state.discover_games) -} - #[tauri::command] #[specta::specta] async fn get_mod_loaders(state: tauri::State<'_, AppState>) -> Result { @@ -123,7 +112,7 @@ fn update_state( data: TData, mutex: &Mutex>, handle: &tauri::AppHandle, -) -> Result { +) { if let Ok(mut mutex_guard) = mutex.lock() { *mutex_guard = Some(data); } @@ -131,9 +120,7 @@ fn update_state( // Sends a signal to make the frontend request an app state refresh. // I would have preferred to just send the state with the signal, // but it seems like Tauri events are really slow for large data. - handle.emit_event(event, ())?; - - Ok(()) + handle.emit_event(event, ()); } #[tauri::command] @@ -213,7 +200,7 @@ fn refresh_single_game( installed_games, &state.installed_games, handle, - )?; + ); Ok(()) } @@ -246,20 +233,19 @@ async fn update_data(handle: tauri::AppHandle, state: tauri::State<'_, AppState> mod_loaders.clone(), &state.mod_loaders, &handle, - )?; + ); - let provider_map = provider::get_map(); - - let steam_dir = SteamDir::locate()?; - let app_info = appinfo::read(steam_dir.path())?; + let provider_map = provider::get_map(&handle); let installed_games: HashMap<_, _> = provider_map .values() .flat_map(|provider| match provider.get_installed_games() { Ok(games) => games, Err(err) => { - // TODO properly handle these errors message to frontend. - eprintln!("Error getting installed games for provider: {}", err); + handle.emit_event( + AppEvent::Error, + format!("Error getting installed games for provider: {err}"), + ); Vec::default() } }) @@ -274,37 +260,24 @@ async fn update_data(handle: tauri::AppHandle, state: tauri::State<'_, AppState> installed_games.clone(), &state.installed_games, &handle, - )?; - - let (discover_games_result, owned_games_result) = futures::future::join( - steam::discover_games::get(&app_info), - futures::future::join_all( - provider_map - .values() - .map(provider::ProviderActions::get_owned_games), - ), - ) - .await; - - let owned_games: Vec = owned_games_result - .into_iter() - .flat_map(result::Result::unwrap_or_default) - .collect(); + ); - let discover_games = discover_games_result.unwrap_or_default(); - update_state( - AppEvent::SyncDiscoverGames, - discover_games, - &state.discover_games, - &handle, - )?; + let owned_games: Vec = futures::future::join_all( + provider_map + .values() + .map(provider::ProviderActions::get_owned_games), + ) + .await + .into_iter() + .flat_map(result::Result::unwrap_or_default) + .collect(); update_state( AppEvent::SyncOwnedGames, owned_games, &state.owned_games, &handle, - )?; + ); Ok(()) } @@ -335,9 +308,9 @@ async fn add_game( installed_games, &state.installed_games, &handle, - )?; + ); - handle.emit_event(AppEvent::GameAdded, game_name)?; + handle.emit_event(AppEvent::GameAdded, game_name); Ok(()) } @@ -360,13 +333,20 @@ async fn remove_game( installed_games, &state.installed_games, &handle, - )?; + ); - handle.emit_event(AppEvent::GameRemoved, game.name)?; + handle.emit_event(AppEvent::GameRemoved, game.name); Ok(()) } +#[tauri::command] +#[specta::specta] +async fn delete_steam_appinfo_cache() -> Result { + let steam_dir = SteamDir::locate()?; + steam::appinfo::delete(steam_dir.path()) +} + #[tauri::command] #[specta::specta] // This command is here just so tauri_specta exports these types. @@ -392,7 +372,6 @@ fn main() { .manage(AppState { installed_games: Mutex::default(), owned_games: Mutex::default(), - discover_games: Mutex::default(), mod_loaders: Mutex::default(), }) .setup(|_app| { @@ -425,7 +404,6 @@ fn main() { update_data, get_installed_games, get_owned_games, - get_discover_games, get_mod_loaders, open_game_folder, install_mod, @@ -436,6 +414,7 @@ fn main() { open_mods_folder, add_game, remove_game, + delete_steam_appinfo_cache, ] ); diff --git a/backend/src/providers/provider.rs b/backend/src/providers/provider.rs index 7bd2411d..17677fe9 100644 --- a/backend/src/providers/provider.rs +++ b/backend/src/providers/provider.rs @@ -4,6 +4,10 @@ use async_trait::async_trait; use enum_dispatch::enum_dispatch; use crate::{ + events::{ + AppEvent, + EventEmitter, + }, installed_game::InstalledGame, owned_game::OwnedGame, providers::{ @@ -49,7 +53,7 @@ where Ok((TProvider::ID.to_string(), mod_loader)) } -fn add_entry(map: &mut Map) +fn add_entry(map: &mut Map, handle: &tauri::AppHandle) where Provider: std::convert::From, { @@ -57,15 +61,15 @@ where Ok((key, value)) => { map.insert(key, value); } - Err(err) => eprintln!("Failed to create map entry: {err}"), + Err(err) => handle.emit_event(AppEvent::Error, format!("Failed to set up provider: {err}")), } } -pub fn get_map() -> Map { +pub fn get_map(handle: &tauri::AppHandle) -> Map { let mut map = Map::new(); - add_entry::(&mut map); - add_entry::(&mut map); + add_entry::(&mut map, handle); + add_entry::(&mut map, handle); map } diff --git a/backend/src/steam/appinfo.rs b/backend/src/steam/appinfo.rs index 864ad678..a3ea5b43 100644 --- a/backend/src/steam/appinfo.rs +++ b/backend/src/steam/appinfo.rs @@ -340,3 +340,7 @@ pub fn read(steam_path: &Path) -> Result { let mut appinfo_file = BufReader::new(fs::File::open(get_path(steam_path))?); SteamAppInfoFile::load(&mut appinfo_file) } + +pub fn delete(steam_path: &Path) -> Result { + Ok(fs::remove_file(get_path(steam_path))?) +} diff --git a/backend/src/steam/command.rs b/backend/src/steam/command.rs index dd660364..cade9250 100644 --- a/backend/src/steam/command.rs +++ b/backend/src/steam/command.rs @@ -8,6 +8,6 @@ use crate::{ pub fn run(command: &str, handle: &tauri::AppHandle) -> Result { open::that_detached(format!("steam://{command}"))?; - handle.emit_event(AppEvent::ExecutedSteamCommand, ())?; + handle.emit_event(AppEvent::ExecutedSteamCommand, ()); Ok(()) } diff --git a/backend/src/steam/discover_games.rs b/backend/src/steam/discover_games.rs deleted file mode 100644 index ab0b6b6a..00000000 --- a/backend/src/steam/discover_games.rs +++ /dev/null @@ -1,26 +0,0 @@ -use super::{ - appinfo::SteamAppInfoFile, - id_lists::{ - self, - SteamGame, - }, -}; -use crate::Result; - -pub async fn get(app_info: &SteamAppInfoFile) -> Result> { - Ok(id_lists::get() - .await? - .into_iter() - .filter(|steam_game| { - if let Ok(id_number) = steam_game.id.parse::() { - if app_info.apps.contains_key(&id_number) { - return false; - } - } else { - return false; - } - - true - }) - .collect()) -} diff --git a/backend/src/steam/mod.rs b/backend/src/steam/mod.rs index e128af87..d9cfddd3 100644 --- a/backend/src/steam/mod.rs +++ b/backend/src/steam/mod.rs @@ -1,5 +1,4 @@ pub mod appinfo; pub mod command; -pub mod discover_games; pub mod id_lists; pub mod thumbnail; diff --git a/backend/tauri.conf.json b/backend/tauri.conf.json index 8e19c9a8..2d87c2bf 100644 --- a/backend/tauri.conf.json +++ b/backend/tauri.conf.json @@ -30,7 +30,7 @@ "dialog": { "all": false, "ask": false, - "confirm": false, + "confirm": true, "message": false, "open": true, "save": false diff --git a/frontend/api/bindings.ts b/frontend/api/bindings.ts index e73f64ee..bfc6a4a3 100644 --- a/frontend/api/bindings.ts +++ b/frontend/api/bindings.ts @@ -26,10 +26,6 @@ export function getOwnedGames() { return invoke()("get_owned_games") } -export function getDiscoverGames() { - return invoke()("get_discover_games") -} - export function getModLoaders() { return invoke()<{ [key: string]: ModLoaderData }>("get_mod_loaders") } @@ -70,12 +66,15 @@ export function removeGame(gameId: string) { return invoke()("remove_game", { gameId }) } +export function deleteSteamAppinfoCache() { + return invoke()("delete_steam_appinfo_cache") +} + export type GameExecutable = { path: string; engine: GameEngine | null; architecture: Architecture | null; operatingSystem: OperatingSystem | null; scriptingBackend: UnityScriptingBackend | null } export type InstalledGame = { id: string; name: string; providerId: ProviderId; discriminator: string | null; steamLaunch: SteamLaunchOption | null; executable: GameExecutable; thumbnailUrl: string | null; availableMods: { [key: string]: boolean } } export type ProviderId = "Steam" | "Manual" -export type AppEvent = "SyncInstalledGames" | "SyncOwnedGames" | "SyncDiscoverGames" | "SyncMods" | "ExecutedSteamCommand" | "GameAdded" | "GameRemoved" -export type SteamGame = { id: string; nsfw: boolean; engine: GameEngineBrand } export type GameEngine = { brand: GameEngineBrand; version: GameEngineVersion | null } +export type AppEvent = "SyncInstalledGames" | "SyncOwnedGames" | "SyncMods" | "ExecutedSteamCommand" | "GameAdded" | "GameRemoved" | "Error" export type Mod = { id: string; name: string; scriptingBackend: UnityScriptingBackend | null; engine: GameEngineBrand | null; kind: ModKind; path: string } export type ModLoaderData = { id: string; path: string; mods: Mod[] } export type OwnedGame = { id: string; providerId: ProviderId; name: string; installed: boolean; osList: OperatingSystem[]; engine: GameEngineBrand; releaseDate: number; thumbnailUrl: string } diff --git a/frontend/app.tsx b/frontend/app.tsx index a8a64b58..688d779e 100644 --- a/frontend/app.tsx +++ b/frontend/app.tsx @@ -1,4 +1,3 @@ -import { DiscoverPage } from "@components/discover/discover-page"; import { InstalledGamesPage } from "./components/installed-games/installed-games-page"; import { ModsPage } from "./components/mods/mods-page"; import { OwnedGamesPage } from "./components/owned-games/owned-games-page"; @@ -10,7 +9,6 @@ import { AppNotifications } from "@components/app-notifications"; const pages = { installedGames: { title: "Installed Games", component: InstalledGamesPage }, ownedGames: { title: "Owned Games", component: OwnedGamesPage }, - discover: { title: "Discover", component: DiscoverPage }, mods: { title: "Mods", component: ModsPage }, settings: { title: "Settings", component: SettingsPage }, }; diff --git a/frontend/components/app-notifications.tsx b/frontend/components/app-notifications.tsx index 344d8ac7..eaf641fb 100644 --- a/frontend/components/app-notifications.tsx +++ b/frontend/components/app-notifications.tsx @@ -1,26 +1,55 @@ import { useAppEvent } from "@hooks/use-app-event"; +import { DefaultMantineColor } from "@mantine/core"; import { Notifications, notifications } from "@mantine/notifications"; +import { IconCheck, IconInfoCircle, IconX } from "@tabler/icons-react"; +import React from "react"; + +type NotificationType = "error" | "success" | "info"; + +function getNotificationColor(type: NotificationType): DefaultMantineColor { + switch (type) { + case "error": + return "red"; + case "info": + return "blue"; + case "success": + return "green"; + } +} +function getNotificationIcon(type: NotificationType): React.ReactNode { + switch (type) { + case "error": + return ; + case "info": + return ; + case "success": + return ; + } +} + +export function showAppNotification(message: string, type: NotificationType) { + notifications.show({ + message, + color: getNotificationColor(type), + icon: getNotificationIcon(type), + }); +} export function AppNotifications() { + useAppEvent("Error", (error) => { + showAppNotification(error, "error"); + }); + useAppEvent("ExecutedSteamCommand", () => { - notifications.show({ - message: "Running steam command...", - color: "blue", - }); + showAppNotification("Running steam command...", "info"); }); useAppEvent("GameAdded", (gameName) => { - notifications.show({ - message: `Successfully added game "${gameName}".`, - color: "green", - }); + showAppNotification(`Successfully added game "${gameName}"`, "success"); }); useAppEvent("GameRemoved", (gameName) => { - notifications.show({ - message: `Removed game "${gameName}".`, - color: "green", - }); + showAppNotification(`Removed game "${gameName}"`, "success"); }); return ; diff --git a/frontend/components/command-button.tsx b/frontend/components/command-button.tsx index c36d94d5..aaff0c14 100644 --- a/frontend/components/command-button.tsx +++ b/frontend/components/command-button.tsx @@ -1,15 +1,17 @@ import { useAsyncCommand } from "@hooks/use-async-command"; import { useLongLoading } from "@hooks/use-long-loading"; import { Button, ButtonProps } from "@mantine/core"; -import { forwardRef } from "react"; +import { forwardRef, useCallback } from "react"; +import { dialog } from "@tauri-apps/api"; interface Props extends ButtonProps { readonly onClick: () => Promise; readonly onSuccess?: () => void; + readonly confirmationText?: string; } function CommandButtonInternal( - { onClick, onSuccess, children, ...props }: Props, + { onClick, onSuccess, confirmationText, children, ...props }: Props, ref: React.ForwardedRef, ) { const [executeCommand, isLoading, success] = useAsyncCommand( @@ -17,6 +19,13 @@ function CommandButtonInternal( onSuccess, ); + const handleClick = useCallback(async () => { + if (confirmationText && !(await dialog.confirm(confirmationText))) { + return; + } + executeCommand(); + }, [executeCommand, confirmationText]); + const isLongLoading = useLongLoading(isLoading); return ( @@ -27,7 +36,7 @@ function CommandButtonInternal( loading={isLongLoading} variant={success ? "filled" : "default"} {...props} - onClick={executeCommand} + onClick={handleClick} > {children} diff --git a/frontend/components/discover/discover-game-card.tsx b/frontend/components/discover/discover-game-card.tsx deleted file mode 100644 index bbcf065f..00000000 --- a/frontend/components/discover/discover-game-card.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { SteamGame } from "@api/bindings"; -import { steamCommands } from "../../util/steam"; -import styles from "./discover.module.css"; -import { Box, Loader, Overlay } from "@mantine/core"; -import { useState } from "react"; - -type Props = { - readonly game: SteamGame; -}; - -export function DiscoverGameCard(props: Props) { - const [isNsfwRevealed, setIsNsfwRevealed] = useState(false); - const [isLoaded, setIsLoaded] = useState(false); - const [isBroken, setIsBroken] = useState(false); - - const isHidden = !isNsfwRevealed && props.game.nsfw; - - return ( - - {isLoaded && isHidden && ( -
- NSFW - Click to reveal -
- )} - {!isBroken && !isLoaded && ( - - - - )} - - isHidden - ? setIsNsfwRevealed(true) - : steamCommands.openStorePage(props.game.id) - } - onError={() => setIsBroken(true)} - onLoad={() => setIsLoaded(true)} - src={ - isBroken - ? "images/fallback-thumbnail.png" - : `https://cdn.cloudflare.steamstatic.com/steam/apps/${props.game.id}/capsule_231x87.jpg` - } - /> -
- ); -} diff --git a/frontend/components/discover/discover-page.tsx b/frontend/components/discover/discover-page.tsx deleted file mode 100644 index 8e47a69a..00000000 --- a/frontend/components/discover/discover-page.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Box, Flex, Paper, Stack, Text } from "@mantine/core"; -import { VirtuosoGrid } from "react-virtuoso"; -import styles from "./discover.module.css"; -import { useMemo } from "react"; -import { RefreshButton } from "@components/refresh-button"; -import { GameEngineBrand } from "@api/bindings"; -import { EngineSelect } from "@components/engine-select"; -import { DiscoverGameCard } from "./discover-game-card"; -import { discoverGamesAtom } from "@hooks/use-data"; -import { useAtomValue } from "jotai"; -import { usePersistedState } from "@hooks/use-persisted-state"; - -export function DiscoverPage() { - const discoverGames = useAtomValue(discoverGamesAtom); - - const [engine, setEngine] = usePersistedState( - "discover-filter-engine", - undefined, - ); - - const filteredGames = useMemo( - () => - (engine - ? discoverGames?.filter((game) => game.engine == engine) - : discoverGames) ?? [], - [discoverGames, engine], - ); - - return ( - - - - {filteredGames.length} games you don't own - - - - - - , - Footer: () => , - }} - itemContent={(_, game) => } - /> - - - ); -} diff --git a/frontend/components/discover/discover.module.css b/frontend/components/discover/discover.module.css deleted file mode 100644 index aa7894ca..00000000 --- a/frontend/components/discover/discover.module.css +++ /dev/null @@ -1,59 +0,0 @@ -div.wrapper { - background-color: var(--mantine-color-dark-6); -} - -.game { - border-radius: var(--mantine-radius-default); - overflow: hidden; - background-color: var(--background-dark); - position: relative; - - img { - display: block; - &:hover { - cursor: pointer; - opacity: 0.75; - } - width: 16em; - height: 6em; - } -} - -.listContainer { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: var(--mantine-spacing-xs); -} - -.spacer { - height: var(--mantine-spacing-xs); -} - -.nsfwImage { - filter: blur(10px); -} - -.nsfwLabel { - position: absolute; - flex-direction: column; - gap: var(--mantine-spacing-xs); - height: 100%; - width: 100%; - display: flex; - justify-content: center; - align-items: center; - font-size: 1em; - pointer-events: none; - text-align: center; - line-height: 0.8; - z-index: 1; - background-color: rgba(0, 0, 0, 0.5); - text-shadow: 0 0 0.5em black; -} - -.loadingImage { - display: flex; - align-items: center; - justify-content: center; -} diff --git a/frontend/components/installed-games/add-game-button.tsx b/frontend/components/installed-games/add-game-button.tsx index 95dca019..c1ea7bed 100644 --- a/frontend/components/installed-games/add-game-button.tsx +++ b/frontend/components/installed-games/add-game-button.tsx @@ -48,7 +48,7 @@ export function AddGame() { onClick={() => setIsOpen(true)} leftSection={} > - Add game... + Add game removeGame(props.game.id)} + confirmationText="Are you sure you want to remove this game from Rai Pal?" onSuccess={props.onClose} leftSection={} > diff --git a/frontend/components/owned-games/fix-owned-games-button.tsx b/frontend/components/owned-games/fix-owned-games-button.tsx new file mode 100644 index 00000000..a0406e6c --- /dev/null +++ b/frontend/components/owned-games/fix-owned-games-button.tsx @@ -0,0 +1,63 @@ +import { deleteSteamAppinfoCache } from "@api/bindings"; +import { CommandButton } from "@components/command-button"; +import { Button, Modal, Stack } from "@mantine/core"; +import { IconHammer } from "@tabler/icons-react"; +import { useCallback, useState } from "react"; + +export function FixOwnedGamesButton() { + const [isModalOpen, setIsModalOpen] = useState(false); + const [showSteamRestartPrompt, setShowSteamRestartPrompt] = useState(false); + + const handleOpen = useCallback(() => { + setIsModalOpen(true); + }, []); + + const handleClose = useCallback(() => { + setIsModalOpen(false); + setShowSteamRestartPrompt(false); + }, []); + + return ( + <> + + + + + Use this if the list is showing games you don't actually own on + Steam. + + + This will reset Steam's cache, and then you'll have to + restart Steam. + + + You'll get an error if the file has already been deleted. + + deleteSteamAppinfoCache()} + onSuccess={() => setShowSteamRestartPrompt(true)} + leftSection={} + > + Delete Steam's Game Info Cache + + {showSteamRestartPrompt && ( + + The cache file has been deleted. Please restart Steam, wait a few + seconds, and then press the refresh button on Rai Pal. + + )} + + + + ); +} diff --git a/frontend/components/owned-games/owned-games-page.tsx b/frontend/components/owned-games/owned-games-page.tsx index e4f04be2..ddf760de 100644 --- a/frontend/components/owned-games/owned-games-page.tsx +++ b/frontend/components/owned-games/owned-games-page.tsx @@ -14,6 +14,7 @@ import { ownedGamesColumns } from "./owned-games-columns"; import { ColumnsSelect } from "@components/columns-select"; import { usePersistedState } from "@hooks/use-persisted-state"; import { TypedSegmentedControl } from "@components/installed-games/typed-segmented-control"; +import { FixOwnedGamesButton } from "./fix-owned-games-button"; const defaultFilter: Record = {}; @@ -64,6 +65,7 @@ export function OwnedGamesPage() { /> ) : null} + ( +export function useAsyncCommand( command: (args: TArgs) => Promise, onSuccess?: (result: TResult) => void, ) { @@ -30,10 +30,7 @@ export function useAsyncCommand( return result; }) .catch((error) => - notifications.show({ - message: `Failed to execute command: ${error}`, - color: "red", - }), + showAppNotification(`Failed to execute command: ${error}`, "error"), ) .finally(() => setIsLoading(false)); }, diff --git a/frontend/hooks/use-data.ts b/frontend/hooks/use-data.ts index 301eecaa..3e487da2 100644 --- a/frontend/hooks/use-data.ts +++ b/frontend/hooks/use-data.ts @@ -2,12 +2,7 @@ import { useEffect } from "react"; import { addGame } from "@api/bindings"; import { event } from "@tauri-apps/api"; import { atom } from "jotai"; -import { - getDiscoverGames, - getInstalledGames, - getModLoaders, - getOwnedGames, -} from "@api/bindings"; +import { getInstalledGames, getModLoaders, getOwnedGames } from "@api/bindings"; import { dataSubscription } from "./use-data-subscription"; import { useUpdateData } from "./use-update-data"; import { useAsyncCommand } from "./use-async-command"; @@ -15,9 +10,6 @@ import { useAsyncCommand } from "./use-async-command"; export const [installedGamesAtom, useInstalledGamesSubscription] = dataSubscription("SyncInstalledGames", getInstalledGames, {}); -export const [discoverGamesAtom, useDiscoverGamesSubscription] = - dataSubscription("SyncDiscoverGames", getDiscoverGames, []); - export const [modLoadersAtom, useModLoadersSubscription] = dataSubscription( "SyncMods", getModLoaders, @@ -33,7 +25,6 @@ export const loadingAtom = atom(false); export function useData() { useInstalledGamesSubscription(); - useDiscoverGamesSubscription(); useModLoadersSubscription(); useOwnedGamesSubscription(); diff --git a/frontend/hooks/use-update-data.ts b/frontend/hooks/use-update-data.ts index 484e8198..1865ae03 100644 --- a/frontend/hooks/use-update-data.ts +++ b/frontend/hooks/use-update-data.ts @@ -2,7 +2,7 @@ import { useCallback } from "react"; import { useSetAtom } from "jotai"; import { updateData } from "@api/bindings"; import { loadingAtom } from "./use-data"; -import { notifications } from "@mantine/notifications"; +import { showAppNotification } from "@components/app-notifications"; export function useUpdateData() { const setIsLoading = useSetAtom(loadingAtom); @@ -11,10 +11,7 @@ export function useUpdateData() { setIsLoading(true); updateData() .catch((error) => { - notifications.show({ - message: `Error finding games: ${error}`, - color: "red", - }); + showAppNotification(`Error finding games: ${error}`, "error"); }) .finally(() => setIsLoading(false)); }, [setIsLoading]); diff --git a/frontend/theme.ts b/frontend/theme.ts index 18442b33..81af347c 100644 --- a/frontend/theme.ts +++ b/frontend/theme.ts @@ -15,5 +15,10 @@ export const theme: MantineThemeOverride = { variant: "default", }, }, + Notification: { + defaultProps: { + withBorder: true, + }, + }, }, }; diff --git a/frontend/util/steam.ts b/frontend/util/steam.ts index 30d27144..55eb7fb9 100644 --- a/frontend/util/steam.ts +++ b/frontend/util/steam.ts @@ -1,9 +1,12 @@ +import { showAppNotification } from "@components/app-notifications"; import { shell } from "@tauri-apps/api"; function runSteamCommand(command: string) { return async function (appId?: string | number) { if (!appId) return; + showAppNotification("Running Steam command...", "info"); + return shell.open(`steam://${command}/${appId}`); }; }