AllenWrench/src/App.tsx

236 lines
7.4 KiB
TypeScript

import { useState } from "react";
import { resolveResource } from "@tauri-apps/api/path";
import { readTextFile } from "@tauri-apps/api/fs";
import { invoke } from '@tauri-apps/api/tauri'
import Modal from "react-modal";
import { platform } from '@tauri-apps/api/os';
import "./App.css";
import { Command } from '@tauri-apps/api/shell'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faGitAlt } from "@fortawesome/free-brands-svg-icons/faGitAlt";
import { PhysicalPosition, appWindow } from "@tauri-apps/api/window";
// TODO: Keyboard usage
const codes = JSON.parse(await readTextFile(await resolveResource("resources/codes.json")));
type Code = {
code: number;
type: string;
text: string;
};
// Set window to above the mouse cursor
invoke('get_mouse_pos', {}).then((posJson: any) => {
let pos = JSON.parse(posJson);
appWindow.innerSize().then((size: any) => {
pos.y -= size.height + 32 /*Standard windows titlebar height*/;
console.log(JSON.stringify(pos));
appWindow.setPosition(new PhysicalPosition(pos.x, pos.y));
});
});
Modal.setAppElement("#root");
function App() {
const [subTopic, setSubTopic] = useState<string>("");
const [configModalOpen, setConfigModalOpen] = useState<boolean>(false);
const [droneId, setDroneId] = useState<string>(() => {
let storedId = localStorage.getItem("droneId");
return (storedId ? storedId : "0000");
});
const getSubjects = (input: Array<Code>) => {
// TODO: Put all single-level elements at bottom
let subjects: Array<string> = [];
input.forEach((code: Code) => {
if (!subjects.includes(code.type)) {
subjects.push(code.type)
}
});
return subjects;
};
const getTopicChildren = (topic: string) => {
let topicChildren: Array<string> = [];
codes.forEach((code: Code) => {
if (code.type == topic) {
if (!topicChildren.includes(code.text)) {
topicChildren.push(code.text)
}
}
});
return topicChildren;
};
const generatePayload = (code: Code) => {
let assembledStr: string = droneId.toString();
let codeId: string = code.code.toString();
if (code.code < 10) {
codeId = "00" + codeId;
}else if (code.code < 100) {
codeId = "0" + codeId;
}
assembledStr += " :: Code " + codeId;
assembledStr += " :: " + code.type;
if (code.text != "."){
assembledStr += " :: " + code.text;
}
console.log(assembledStr);
return assembledStr;
};
const handleSubClick = (topic: string, index: number) => {
let text = getTopicChildren(topic)[index];
let filtered = codes.filter((code: Code) => code.type === topic && code.text === text);
if (filtered.length == 1) {
let payload: string = generatePayload(filtered[0]);
setSubTopic("");
invoke('type_str', {input: payload});
}
};
const SubMenu = () => {
if (subTopic == "") {
return (<div></div>);
} else {
return (
<div className="col-8 menu">
{getTopicChildren(subTopic).map((text: string, index: number, topics: Array<string>) => {
let displayText: string = text;
displayText = displayText.replace(/.*:: /, "");
if (displayText == "") {
displayText = "...";
}
return(
<div
className={`row sub-element ${(topics.length-1 == index) ? "last" : ""}`}
key={index}
onClick={() => {
handleSubClick(subTopic, index)
}}
>
{displayText}
</div>
)
})}
</div>
);
}
}
const handleTopicClick = (type: string) => {
let filtered = codes.filter((code: Code) => code.type === type);
if (filtered.length == 1) {
let assembledStr: string = generatePayload(filtered[0]);
setSubTopic("");
invoke('type_str', {input: assembledStr});
}else{
// Open Submenu for type
setSubTopic(type);
}
};
return (
<div className="container">
<Modal
isOpen={configModalOpen}
contentLabel="Configuration"
overlayClassName="config-overlay"
className="config-modal"
>
<div className="row">
<h1 className="col-12">
<a href="">Drone Input Tool</a>
</h1>
</div>
<div className="row">
<h2 className="col-12">
Hexcorp Drone Interface
</h2>
</div>
<div className="row">
<div className="col-6 text-end">
Drone ID
</div>
<div className="col-6 drone-id-input">
<input
type="text"
value={droneId}
onChange={(e) => {
setDroneId(e.target.value);
localStorage.setItem("droneId", e.target.value);
}}
/>
</div>
{/* TODO: Optional auto-send on pre-made messages */}
</div>
<div className="row modal-footer">
<div className="col-4" onClick={() => {
let url = "https://git.corrupt.link/liz/AllenWrench";
platform().then((os: string) => {
switch(os) {
case "win32":
new Command("open-link-win", ["-Command", "Start-Process", `${url}`]).spawn();
break;
case "linux":
new Command("open-link-linux", ["-c", "xdg-open", `${url}`]).spawn();
break;
case "macos":
new Command("open-link-macos", ["-c", "open", `${url}`]).spawn();
break;
default:
console.log(`Unknown OS: ${os}`);
break;
}
setConfigModalOpen(false);
});
}}>
<FontAwesomeIcon className="icon" icon={faGitAlt} size="2x" />
</div>
<div className="col-8 text-end modal-close" onClick={() => {
setConfigModalOpen(false);
}}>
Close
</div>
</div>
</Modal>
<div className="row">
<div className="col-4">
{getSubjects(codes).map((type: string, index: number) => {
let dispType: string = type;
let filtered = codes.filter((code: Code) => code.type === type);
if (filtered.length == 1) {
dispType += ((filtered[0].text == "")?" :: ...":((filtered[0].text == ".")?"":(" :: "+filtered[0].text)));
}
return (
<input
className={`item ${(filtered.length == 1)?"individual":""}`}
key={index}
type="button"
onClick={() => {
handleTopicClick(type)
}}
value={dispType}
/>
);
})}
</div>
<SubMenu/>
</div>
<div className="row footer">
<div className="col-12 footer-content" onClick={()=>{
setSubTopic("");
console.log(`Config modal open ${configModalOpen}`);
setConfigModalOpen(!configModalOpen);
}}>
{droneId}
</div>
</div>
</div>
);
}
export default App;