Skip to content

Commit

Permalink
Merge pull request #35 from yep-tf2/tauri_configurable-demos-dir
Browse files Browse the repository at this point in the history
Add UI for configuring the demos directory
  • Loading branch information
Narcha committed May 5, 2023
2 parents 71e1310 + f88f80c commit 923bbe3
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 9 deletions.
120 changes: 119 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tauri = { version = "^1.3", features = ["api-all"] }
log = "^0.4"
bitbuffer = "0.10.9"
trash = "3.0.0"
steamlocate = "1.1.0"
steamid-ng = "1.0.0"
num-traits = "0.2.15"
num-derive = "0.3.3"
Expand Down
23 changes: 23 additions & 0 deletions src-tauri/src/commands/files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use serde::Serialize;
use steamlocate::SteamDir;
use tauri::State;

use crate::AppState;

#[derive(Clone, Debug, Serialize)]
pub enum TF2DirError {
/// Could not find Steam installed on the current system
SteamNotFound,
/// Found Steam, but could not find TF2 within the Steam installation directory
Tf2NotFound,
}

#[tauri::command]
pub fn get_tf2_dir(_state: State<'_, AppState>) -> Result<String, TF2DirError> {
const TF2_ID: u32 = 440;

let mut steam_dir = SteamDir::locate().ok_or(TF2DirError::SteamNotFound)?;
let tf2_dir = steam_dir.app(&TF2_ID).ok_or(TF2DirError::Tf2NotFound)?;

Ok(String::from(tf2_dir.path.to_string_lossy()))
}
1 change: 1 addition & 0 deletions src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod demos;
pub mod files;
pub mod rcon;
1 change: 1 addition & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ fn main() {
commands::demos::rename_demo,
commands::demos::set_demo_events,
commands::demos::set_demo_tags,
commands::files::get_tf2_dir,
commands::rcon::init_rcon,
commands::rcon::send_command,
])
Expand Down
11 changes: 10 additions & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@
},
"tauri": {
"allowlist": {
"all": true
"all": true,
"path": {
"all": true
},
"fs": {
"all": true,
"scope": [
"**"
]
}
},
"bundle": {
"active": true,
Expand Down
4 changes: 4 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ export async function getDemoDetails(demoPath: string) {
});
}

export async function getTf2Dir() {
return invoke<string>("get_tf2_dir");
}

export async function initRcon(password: string) {
return invoke<void>("init_rcon", { password });
}
Expand Down
28 changes: 26 additions & 2 deletions src/views/home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import BottomBar from "./BottomBar";
import { NavbarPortal, NavbarButton } from "../../AppShell";
import { getDemosInDirectory } from "../../api";
import { Async, Fill } from "../../components";
import useStore from "../../hooks/useStore";
import { getDefaultDemosDir } from "../settings/storage";

const PADDING_SIZE = 16;

Expand Down Expand Up @@ -192,10 +194,28 @@ function MainView({ demos }: { demos: Demo[] }) {
}

export default function HomeViewAsyncWrapper() {
const [storedDemoPath, setStoredDemoPath] = useStore("demoPath");

if (storedDemoPath === undefined) {
// Used as fallback in case the user hasn't configured the demos directory yet
getDefaultDemosDir()
.then(dir => {
if (storedDemoPath === undefined) {
setStoredDemoPath(dir);
}
return dir;
})
.catch(_err => {
// Failed to get the default directory. At this point, there's nothing reasonable
// we can do other than alert the user and prompt them to set the directory themselves
return;
});
}

return (
<Async
promiseFn={getDemosInDirectory}
args={["/home/rasmus/steamapps-common/Team Fortress 2/tf/demos"]}
args={[storedDemoPath ?? ""]}
loading={
<Fill>
<Loader size="lg" variant="dots" />
Expand All @@ -204,7 +224,11 @@ export default function HomeViewAsyncWrapper() {
error={(error) => (
<Fill>
<Alert color="red">
An error occured while loading this demo directory: {String(error)}
An error occurred while scanning for demo files. Is the demo storage
directory set?
<div>
Error: {String(error)}
</div>
</Alert>
</Fill>
)}
Expand Down
60 changes: 55 additions & 5 deletions src/views/settings/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,62 @@
import { Link } from "react-router-dom";
import useStore from "../../hooks/useStore";
import { Button, createStyles, ScrollArea, TextInput } from "@mantine/core";
import { open as dialogOpen } from "@tauri-apps/api/dialog";

const useStyles = createStyles({
root: {
margin: "12px",
display: "flex",
flexDirection: "column",
gap: "8px",
height: "100%"
},
row: {
display: "flex",
flexDirection: "row",
gap: "8px",
},
rowLabel: {
width: 160,
fontSize: "16pt",
},
});

export default function SettingsView() {
const [demoPath, setDemoPath] = useStore("demoPath");

const { classes } = useStyles();

return (
<div>
<div className={classes.root}>
<h1>Settings</h1>
<div>
<Link to="/">Back</Link>
</div>

<ScrollArea>
<span className={classes.row}>
<span className={classes.rowLabel}>
<label>Demos Folder</label>
</span>
<Button
onClick={() => {
dialogOpen({
directory: true,
defaultPath: demoPath,
title: "Select Demo Storage Folder",
})
.then((value) => {
if (value !== null && value !== "") {
// Don't set the path if the user cancelled the dialog
setDemoPath(value as string);
}
return;
})
.catch((error) => console.error(error));
}}
>
Select a folder...
</Button>
<TextInput value={demoPath ?? ""} disabled={true}></TextInput>
</span>
</ScrollArea>
</div>
);
}
15 changes: 15 additions & 0 deletions src/views/settings/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { homeDir, join } from "@tauri-apps/api/path";
import { getTf2Dir } from "../../api";

/**
* Gets the default platform-specific demo file storage directory, assuming Steam and TF2 are installed on the local
* machine. Falls back to the user's home directory if neither is found.
*/
export async function getDefaultDemosDir(): Promise<string> {
const userHomeDir = await homeDir();

// Try getting the TF2 game directory, falling back to the user home directory if it couldn't be found
return await getTf2Dir()
.then((tf2Dir) => join(tf2Dir, "tf", "demos"))
.catch((_error) => userHomeDir);
}

0 comments on commit 923bbe3

Please sign in to comment.