Skip to content

Commit

Permalink
refactor: decouple Controller component
Browse files Browse the repository at this point in the history
* 275ms delayPromise no longer needed
* loading state no longer needed

Signed-off-by: eXhumer <[email protected]>
  • Loading branch information
eXhumer committed Oct 26, 2024
1 parent 4cea9c0 commit dcc05d9
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 63 deletions.
2 changes: 1 addition & 1 deletion backend/Cargo.lock

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

2 changes: 1 addition & 1 deletion backend/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "controller-tools"
version = "2.0.1"
version = "2.0.2"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "controller-tools",
"version": "2.0.1",
"version": "2.0.2",
"description": "The missing game controller menu. Displays the current battery % and charging status",
"type": "module",
"scripts": {
Expand Down
54 changes: 54 additions & 0 deletions src/components/Controller.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
PanelSectionRow,
gamepadDialogClasses,
joinClassNames,
} from "@decky/ui";

import { IconContext } from "react-icons";
import { BiBluetooth, BiUsb } from "react-icons/bi";

import BatteryIcon from "./BatteryIcon";
import VendorIcon from "./VendorIcon";
import { IController } from "../types";

const FieldWithSeparator = joinClassNames(gamepadDialogClasses.Field, gamepadDialogClasses.WithBottomSeparatorStandard);

type ControllerProps = {
controller: IController;
};

const Controller = ({ controller }: ControllerProps) => {
return (
<PanelSectionRow>
<div className={FieldWithSeparator}>
<div className={gamepadDialogClasses.FieldLabelRow}>
<div className={gamepadDialogClasses.FieldLabel}>
<IconContext.Provider value={{ style: { verticalAlign: 'middle', marginRight: '10px' } }}>
{controller.bluetooth ? <BiBluetooth /> : <BiUsb />}
</IconContext.Provider>
<IconContext.Provider value={{ style: { verticalAlign: 'middle', marginRight: '5px' } }}>
<VendorIcon controller={controller}/>
</IconContext.Provider>
{controller.name}
</div>
{
(controller.capacity > 0 || controller.status !== "unknown") &&
<div className={gamepadDialogClasses.FieldChildrenInner}>
{
// only show battery capacity for non-MS vendors unless capacity is > 0 and over BT
// since we don't have the battery capacity yet for Xbox over USB
(controller.vendorId != 0x045E || (controller.capacity > 0 && controller.bluetooth)) &&
<span style={{ display: "inline-block", textAlign: "right", }}>{controller.capacity}%</span>
}
<IconContext.Provider value={{ style: { verticalAlign: 'middle', marginLeft: "6px" }, size: '2em' }}>
<BatteryIcon controller={controller}/>
</IconContext.Provider>
</div>
}
</div>
</div>
</PanelSectionRow>
);
};

export default Controller;
47 changes: 9 additions & 38 deletions src/components/ControllersView.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,20 @@
import { gamepadDialogClasses, joinClassNames, PanelSectionRow } from "@decky/ui";
import { IController } from "../types";
import { IconContext } from "react-icons";
import { BiBluetooth, BiUsb } from "react-icons/bi";
import VendorIcon from "./VendorIcon";
import BatteryIcon from "./BatteryIcon";

const FieldWithSeparator = joinClassNames(gamepadDialogClasses.Field, gamepadDialogClasses.WithBottomSeparatorStandard);
import Controller from "./Controller";

type ControllersViewProps = {
controllers: IController[];
};

const ControllersView = ({ controllers }: ControllersViewProps) => {
return (
controllers.sort((a, b) => a.name.localeCompare(b.name)).map((controller) => (
<PanelSectionRow key={controller.productId}>
<div className={FieldWithSeparator}>
<div className={gamepadDialogClasses.FieldLabelRow}>
<div className={gamepadDialogClasses.FieldLabel}>
<IconContext.Provider value={{ style: { verticalAlign: 'middle', marginRight: '10px' } }}>
{controller.bluetooth ? <BiBluetooth /> : <BiUsb />}
</IconContext.Provider>
<IconContext.Provider value={{ style: { verticalAlign: 'middle', marginRight: '5px' } }}>
<VendorIcon controller={controller}/>
</IconContext.Provider>
{controller.name}
</div>
{
(controller.capacity > 0 || controller.status !== "unknown") &&
<div className={gamepadDialogClasses.FieldChildrenInner}>
{
// only show battery capacity for non-MS vendors unless capacity is > 0 and over BT
// since we don't have the battery capacity yet for Xbox over USB
(controller.vendorId != 0x045E || (controller.capacity > 0 && controller.bluetooth)) &&
<span style={{ display: "inline-block", textAlign: "right", }}>{controller.capacity}%</span>
}
<IconContext.Provider value={{ style: { verticalAlign: 'middle', marginLeft: "6px" }, size: '2em' }}>
<BatteryIcon controller={controller}/>
</IconContext.Provider>
</div>
}
</div>
</div>
</PanelSectionRow>
))
controllers
.sort((a, b) => a.name.localeCompare(b.name))
.map(controller => (
<Controller
controller={controller}
key={`${controller.vendorId}:${controller.productId}`}
/>
))
);
};

Expand Down
8 changes: 2 additions & 6 deletions src/components/NoControllersView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@ import { gamepadDialogClasses, joinClassNames, PanelSectionRow } from "@decky/ui

const FieldWithSeparator = joinClassNames(gamepadDialogClasses.Field, gamepadDialogClasses.WithBottomSeparatorStandard);

type NoControllersViewProps = {
loading: boolean;
};

const NoControllersView = ({ loading }: NoControllersViewProps) => {
const NoControllersView = () => {
return (
<PanelSectionRow>
<div className={FieldWithSeparator}>
<div className={gamepadDialogClasses.FieldLabelRow}>
<div className={gamepadDialogClasses.FieldLabel}>
{loading ? 'Loading...' : 'No controllers found'}
No controllers found
</div>
</div>
</div>
Expand Down
24 changes: 8 additions & 16 deletions src/components/PluginContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,15 @@ import * as backend from "../backend";
import { IController } from "../types";
import ControllersView from "./ControllersView";

const delayPromise = <T,>(value: T) => {
return new Promise<T>(resolve => {
setTimeout(() => resolve(value), 275);
});
};

const PluginContent = () => {
const [debug, setDebug] = useState<boolean>(false);
const [notifications, setNotifications] = useState<boolean>(true);
const [loading, setLoading] = useState<boolean>(false);
const [controllers, setControllers] = useState<IController[]>([]);

// For fetching controller & settings data on render
useEffect(() => {
backend.getControllers()
.then((controllers) => { setControllers(controllers); });
.then(controllers => { setControllers(controllers); });

backend.getDebugSetting()
.then(debug => { setDebug(debug); });
Expand All @@ -34,11 +27,10 @@ const PluginContent = () => {
.then(notifications => { setNotifications(notifications); });
}, []);

const onRefresh = async () => {
setControllers([]);
setLoading(true);
setControllers(await delayPromise(backend.getControllers()));
setLoading(false);
const onRefresh = () => {
backend
.getControllers()
.then(controllers => { setControllers(controllers); });
};

const onDebugChange = (e: boolean) => {
Expand All @@ -60,9 +52,9 @@ const PluginContent = () => {
return (
<PanelSection title="Controllers">
{controllers.length === 0 ?
<NoControllersView loading={loading}/> :
<ControllersView controllers={controllers}/>}
<RefreshButton onClick={onRefresh}/>
<NoControllersView /> :
<ControllersView controllers={controllers} />}
<RefreshButton onClick={onRefresh} />
<SettingsMenu
debug={debug}
notifications={notifications}
Expand Down

0 comments on commit dcc05d9

Please sign in to comment.