Skip to content

Commit

Permalink
Add export/save for spawn file. Sections for spawn editor.
Browse files Browse the repository at this point in the history
Signed-off-by: Neloreck <Neloreck@gmail.com>
  • Loading branch information
Neloreck committed Mar 3, 2024
1 parent eb0353b commit 87dc9b8
Show file tree
Hide file tree
Showing 17 changed files with 214 additions and 17 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
run: cargo make build-cli-release

- name: Upload CLI build artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: xrf-cli
Expand Down Expand Up @@ -79,7 +79,7 @@ jobs:
with:
ref: nightly

- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: xrf-cli

Expand All @@ -89,7 +89,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload --clobber nightly xrf-cli.exe

- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: xrf-application

Expand Down
2 changes: 1 addition & 1 deletion bin/xrf-application/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ edition = "2021"
tauri-build = { version = "1", features = [] }

[dependencies]
tauri = { version = "1", features = [ "dialog-ask", "dialog-open", "dialog-confirm", "shell-open"] }
tauri = { version = "1", features = [ "dialog-save", "dialog-ask", "dialog-open", "dialog-confirm", "shell-open"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
xray-db = { path = "../../crates/xray-db" }
Expand Down
7 changes: 6 additions & 1 deletion bin/xrf-application/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

mod spawn_file;

use crate::spawn_file::{close_spawn_file, get_spawn_file, open_spawn_file, SpawnFileState};
use crate::spawn_file::{
close_spawn_file, export_spawn_file, get_spawn_file, open_spawn_file, save_spawn_file,
SpawnFileState,
};
use std::env;
use std::sync::{Arc, Mutex};

Expand All @@ -14,6 +17,8 @@ fn main() {
.invoke_handler(tauri::generate_handler![
get_spawn_file,
open_spawn_file,
save_spawn_file,
export_spawn_file,
close_spawn_file
])
.manage(SpawnFileState {
Expand Down
39 changes: 39 additions & 0 deletions bin/xrf-application/src/spawn_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,45 @@ pub async fn open_spawn_file(
}
}

#[tauri::command]
pub fn save_spawn_file(path: &str, state: tauri::State<'_, SpawnFileState>) -> Result<(), String> {
log::info!("Saving spawn file");

let lock = state.file.lock().unwrap();

if lock.is_some() {
let file: &SpawnFile = lock.as_ref().unwrap();

match file.write_to_path::<SpawnByteOrder>(Path::new(path)) {
Ok(_) => Ok(()),
Err(error) => Err(error.to_string()),
}
} else {
Err(String::from("No spawn file open for saving"))
}
}

#[tauri::command]
pub async fn export_spawn_file(
path: &str,
state: tauri::State<'_, SpawnFileState>,
) -> Result<(), String> {
log::info!("Saving spawn file");

let lock = state.file.lock().unwrap();

if lock.is_some() {
let file: &SpawnFile = lock.as_ref().unwrap();

match file.export_to_path::<SpawnByteOrder>(Path::new(path)) {
Ok(_) => Ok(()),
Err(error) => Err(error.to_string()),
}
} else {
Err(String::from("No spawn file open for saving"))
}
}

#[tauri::command]
pub fn close_spawn_file(state: tauri::State<'_, SpawnFileState>) {
log::info!("Closing spawn file");
Expand Down
1 change: 1 addition & 0 deletions bin/xrf-application/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"dialog": {
"ask": true,
"confirm": true,
"save": true,
"open": true
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function SpawnEditorRouter(): ReactElement {
<Route path={"/"} element={<SpawnEditorNavigatorPage />} />

<Route
path={"editor"}
path={"editor/*"}
element={
<SpawnEditorProvider>
<SpawnEditorPage />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
import { Grid } from "@mui/material";
import { ReactElement } from "react";
import { Route, Routes } from "react-router-dom";

import { SpawnEditorAlife } from "@/applications/spawn_editor/components/editor/SpawnEditorAlife";
import { SpawnEditorArtefacts } from "@/applications/spawn_editor/components/editor/SpawnEditorArtefacts";
import { SpawnEditorGeneral } from "@/applications/spawn_editor/components/editor/SpawnEditorGeneral";
import { SpawnEditorGraphs } from "@/applications/spawn_editor/components/editor/SpawnEditorGraphs";
import { SpawnEditorHeader } from "@/applications/spawn_editor/components/editor/SpawnEditorHeader";
import { SpawnEditorMenu } from "@/applications/spawn_editor/components/editor/SpawnEditorMenu";
import { SpawnEditorPatrols } from "@/applications/spawn_editor/components/editor/SpawnEditorPatrols";

export function SpawnEditor(): ReactElement {
return (
<Grid
justifyContent={"center"}
alignItems={"center"}
direction={"column"}
direction={"row"}
container={true}
width={"100%"}
height={"100%"}
>
<SpawnEditorMenu></SpawnEditorMenu>
<SpawnEditorMenu />

<Routes>
<Route path={"/"} element={<SpawnEditorGeneral />} />
<Route path={"/general"} element={<SpawnEditorGeneral />} />
<Route path={"/header"} element={<SpawnEditorHeader />} />
<Route path={"/alife"} element={<SpawnEditorAlife />} />
<Route path={"/artefacts"} element={<SpawnEditorArtefacts />} />
<Route path={"/patrols"} element={<SpawnEditorPatrols />} />
<Route path={"/graph"} element={<SpawnEditorGraphs />} />
</Routes>
</Grid>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Grid } from "@mui/material";
import { ReactElement } from "react";

export function SpawnEditorAlife(): ReactElement {
return (
<Grid justifyContent={"center"} alignItems={"center"} width={"auto"} height={"100%"} flexGrow={1} container>
Alife section
</Grid>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Grid } from "@mui/material";
import { ReactElement } from "react";

export function SpawnEditorArtefacts(): ReactElement {
return (
<Grid justifyContent={"center"} alignItems={"center"} width={"auto"} height={"100%"} flexGrow={1} container>
Artefacts section
</Grid>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Grid } from "@mui/material";
import { ReactElement } from "react";

export function SpawnEditorGeneral(): ReactElement {
return (
<Grid justifyContent={"center"} alignItems={"center"} width={"auto"} height={"100%"} flexGrow={1} container>
General section
</Grid>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Grid } from "@mui/material";
import { ReactElement } from "react";

export function SpawnEditorGraphs(): ReactElement {
return (
<Grid justifyContent={"center"} alignItems={"center"} width={"auto"} height={"100%"} flexGrow={1} container>
Graphs section
</Grid>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Grid } from "@mui/material";
import { ReactElement } from "react";

export function SpawnEditorHeader(): ReactElement {
return (
<Grid justifyContent={"center"} alignItems={"center"} width={"auto"} height={"100%"} flexGrow={1} container>
Header section
</Grid>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,52 @@ import { default as ImportExportIcon } from "@mui/icons-material/ImportExport";
import { default as MemoryIcon } from "@mui/icons-material/Memory";
import { default as SaveIcon } from "@mui/icons-material/Save";
import { Divider, Drawer, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from "@mui/material";
import { dialog } from "@tauri-apps/api";
import { useManager } from "dreamstate";
import { ReactElement } from "react";
import { ReactElement, useCallback } from "react";
import { NavigateFunction, redirect, useNavigate } from "react-router-dom";

import { SpawnFileManager } from "@/applications/spawn_editor/store/spawn";
import { Optional } from "@/core/types/general";

export function SpawnEditorMenu({
spawnContext: { spawnActions, spawnFile } = useManager(SpawnFileManager),
}): ReactElement {
const navigate: NavigateFunction = useNavigate();

const onSaveClicked = useCallback(async () => {
const path: Optional<string> = await dialog.save({
title: "Save spawn file",
filters: [{ name: "spawn", extensions: ["spawn"] }],
});

if (path) {
await spawnActions.saveSpawnFile(path);
}
}, [spawnActions, redirect]);

const onExportClicked = useCallback(async () => {
const path: Optional<string> = (await dialog.open({
title: "Export spawn file",
directory: true,
})) as Optional<string>;

if (path) {
await spawnActions.exportSpawnFile(path);
}
}, [spawnActions, redirect]);

const onCloseClicked = useCallback(() => {
navigate("general", { replace: true });

return spawnActions.closeSpawnFile();
}, [spawnActions, redirect]);

return (
<Drawer variant={"permanent"} open={true}>
<Drawer variant={"permanent"} open={true} sx={{ height: "100%" }} PaperProps={{ sx: { position: "relative" } }}>
<List>
<ListItem disablePadding>
<ListItemButton>
<ListItemButton onClick={() => navigate("general", { replace: true })}>
<ListItemIcon>
<CottageIcon />
</ListItemIcon>
Expand All @@ -30,7 +63,7 @@ export function SpawnEditorMenu({
<List>
{["Header", "Alife", "Artefacts", "Patrols", "Graph"].map((text) => (
<ListItem key={text} disablePadding>
<ListItemButton>
<ListItemButton onClick={() => navigate(text.toLowerCase(), { replace: true })}>
<ListItemIcon>
<MemoryIcon />
</ListItemIcon>
Expand All @@ -44,7 +77,7 @@ export function SpawnEditorMenu({

<List>
<ListItem disablePadding>
<ListItemButton disabled={spawnFile.isLoading}>
<ListItemButton disabled={spawnFile.isLoading} onClick={onSaveClicked}>
<ListItemIcon>
<SaveIcon />
</ListItemIcon>
Expand All @@ -53,7 +86,7 @@ export function SpawnEditorMenu({
</ListItem>

<ListItem disablePadding>
<ListItemButton disabled={spawnFile.isLoading}>
<ListItemButton disabled={spawnFile.isLoading} onClick={onExportClicked}>
<ListItemIcon>
<ImportExportIcon />
</ListItemIcon>
Expand All @@ -62,7 +95,7 @@ export function SpawnEditorMenu({
</ListItem>

<ListItem disablePadding>
<ListItemButton disabled={spawnFile.isLoading} onClick={spawnActions.closeSpawnFile}>
<ListItemButton disabled={spawnFile.isLoading} onClick={onCloseClicked}>
<ListItemIcon>
<CloseIcon />
</ListItemIcon>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Grid } from "@mui/material";
import { ReactElement } from "react";

export function SpawnEditorPatrols(): ReactElement {
return (
<Grid justifyContent={"center"} alignItems={"center"} width={"auto"} height={"100%"} flexGrow={1} container>
Patrols section
</Grid>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { Logger } from "@/lib/logging";
export interface ISpawnFileContext {
spawnActions: {
openSpawnFile: (path: string) => Promise<void>;
saveSpawnFile: (path: string) => Promise<void>;
exportSpawnFile: (path: string) => Promise<void>;
closeSpawnFile: () => Promise<void>;
resetSpawnFile: () => void;
};
Expand All @@ -19,6 +21,8 @@ export class SpawnFileManager extends ContextManager<ISpawnFileContext> {
public context: ISpawnFileContext = {
spawnActions: createActions({
openSpawnFile: (path) => this.openSpawnFile(path),
saveSpawnFile: (path) => this.saveSpawnFile(path),
exportSpawnFile: (path) => this.exportSpawnFile(path),
closeSpawnFile: () => this.closeSpawnFile(),
resetSpawnFile: () => this.setContext({ spawnFile: createLoadable(null) }),
}),
Expand Down Expand Up @@ -67,4 +71,40 @@ export class SpawnFileManager extends ContextManager<ISpawnFileContext> {
this.log.error("Failed to close spawn file:", error);
}
}

public async exportSpawnFile(path: string): Promise<void> {
this.log.info("Exporting spawn file:", path);

this.assertSpawnFileIsOpen();

try {
this.setContext({ spawnFile: this.context.spawnFile.asLoading() });
await invoke(ECommand.EXPORT_SPAWN_FILE, { path });
this.setContext({ spawnFile: this.context.spawnFile.asReady() });
} catch (error) {
this.log.error("Failed to close spawn file:", error);
this.setContext({ spawnFile: this.context.spawnFile.asReady() });
}
}

public async saveSpawnFile(path: string): Promise<void> {
this.log.info("Saving spawn file:", path);

this.assertSpawnFileIsOpen();

try {
this.setContext({ spawnFile: this.context.spawnFile.asLoading() });
await invoke(ECommand.SAVE_SPAWN_FILE, { path });
this.setContext({ spawnFile: this.context.spawnFile.asReady() });
} catch (error) {
this.log.error("Failed to close spawn file:", error);
this.setContext({ spawnFile: this.context.spawnFile.asReady() });
}
}

public assertSpawnFileIsOpen(): asserts this is { context: { spawnFile: { value: unknown } } } {
if (this.context.spawnFile.value === null) {
throw new Error("Unexpected operation, spawn file is null.");
}
}
}
2 changes: 2 additions & 0 deletions bin/xrf-ui/src/lib/ipc/command.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export enum ECommand {
OPEN_SPAWN_FILE = "open_spawn_file",
CLOSE_SPAWN_FILE = "close_spawn_file",
SAVE_SPAWN_FILE = "save_spawn_file",
EXPORT_SPAWN_FILE = "export_spawn_file",
GET_SPAWN_FILE = "get_spawn_file",
}
Loading

0 comments on commit 87dc9b8

Please sign in to comment.