aboutsummaryrefslogtreecommitdiff
path: root/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/mic.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/mic.jsx')
-rwxr-xr-xusers/ryan/modules/simple-bar/simple-bar-source/lib/components/data/mic.jsx152
1 files changed, 152 insertions, 0 deletions
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/mic.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/mic.jsx
new file mode 100755
index 0000000..5c071a8
--- /dev/null
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/mic.jsx
@@ -0,0 +1,152 @@
1import * as Uebersicht from "uebersicht";
2import * as DataWidget from "./data-widget.jsx";
3import * as DataWidgetLoader from "./data-widget-loader.jsx";
4import * as Icons from "../icons/icons.jsx";
5import { SuspenseIcon } from "../icons/icon.jsx";
6import useWidgetRefresh from "../../hooks/use-widget-refresh";
7import useServerSocket from "../../hooks/use-server-socket";
8import * as Utils from "../../utils";
9import { useSimpleBarContext } from "../simple-bar-context.jsx";
10
11const { React } = Uebersicht;
12
13export { micStyles as styles } from "../../styles/components/data/mic";
14
15const DEFAULT_REFRESH_FREQUENCY = 20000;
16
17/**
18 * Mic widget component.
19 * @returns {JSX.Element} The rendered mic widget.
20 */
21export const Widget = React.memo(() => {
22 const { displayIndex, settings } = useSimpleBarContext();
23 const { widgets, micWidgetOptions } = settings;
24 const { micWidget } = widgets;
25 const { refreshFrequency, showOnDisplay, showIcon } = micWidgetOptions;
26
27 // Determine the refresh frequency for the widget.
28 const refresh = React.useMemo(
29 () =>
30 Utils.getRefreshFrequency(refreshFrequency, DEFAULT_REFRESH_FREQUENCY),
31 [refreshFrequency],
32 );
33
34 // Determine if the widget should be visible.
35 const visible =
36 Utils.isVisibleOnDisplay(displayIndex, showOnDisplay) && micWidget;
37
38 const [state, setState] = React.useState();
39 const [loading, setLoading] = React.useState(visible);
40 const { volume: _volume } = state || {};
41 const [volume, setVolume] = React.useState(_volume && parseInt(_volume, 10));
42 const [dragging, setDragging] = React.useState(false);
43
44 /**
45 * Reset the widget state.
46 */
47 const resetWidget = () => {
48 setState(undefined);
49 setLoading(false);
50 };
51
52 /**
53 * Fetch the current microphone volume.
54 */
55 const getMic = React.useCallback(async () => {
56 if (!visible) return;
57 const volume = await Utils.cachedRun(
58 `osascript -e 'input volume of (get volume settings)'`,
59 refresh,
60 );
61 setState({ volume: Utils.cleanupOutput(volume) });
62 setLoading(false);
63 }, [visible, refresh]);
64
65 // Use server socket to get mic data.
66 useServerSocket("mic", visible, getMic, resetWidget, setLoading);
67 // Refresh the widget periodically.
68 useWidgetRefresh(visible, getMic, refresh);
69
70 // Update the mic volume when dragging state changes.
71 React.useEffect(() => {
72 if (!dragging) setMic(volume);
73 }, [dragging, volume]);
74
75 // Update the volume state when the fetched volume changes.
76 React.useEffect(() => {
77 setVolume((currentVolume) => {
78 if (_volume && currentVolume !== parseInt(_volume, 10)) {
79 return parseInt(_volume, 10);
80 }
81 return currentVolume;
82 });
83 }, [_volume]);
84
85 if (loading) return <DataWidgetLoader.Widget className="mic" />;
86 if (!state || volume === undefined || _volume === "missing value")
87 return null;
88
89 const Icon = !volume ? Icons.MicOff : Icons.MicOn;
90
91 /**
92 * Handle volume change event.
93 * @param {React.ChangeEvent<HTMLInputElement>} e - The change event.
94 */
95 const onChange = (e) => {
96 const value = parseInt(e.target.value, 10);
97 setVolume(value);
98 };
99
100 /**
101 * Handle mouse down event on the slider.
102 */
103 const onMouseDown = () => setDragging(true);
104
105 /**
106 * Handle mouse up event on the slider.
107 */
108 const onMouseUp = () => setDragging(false);
109
110 const formattedVolume = `${volume.toString().padStart(2, "0")}%`;
111
112 const classes = Utils.classNames("mic", {
113 "mic--dragging": dragging,
114 });
115
116 return (
117 <DataWidget.Widget classes={classes} disableSlider>
118 <div className="mic__display">
119 {showIcon && (
120 <SuspenseIcon>
121 <Icon />
122 </SuspenseIcon>
123 )}
124 <span className="mic__value">{formattedVolume}</span>
125 </div>
126 <div className="mic__slider-container">
127 <input
128 type="range"
129 min="0"
130 max="100"
131 step="1"
132 value={volume}
133 className="mic__slider"
134 onMouseDown={onMouseDown}
135 onMouseUp={onMouseUp}
136 onChange={onChange}
137 />
138 </div>
139 </DataWidget.Widget>
140 );
141});
142
143Widget.displayName = "Mic";
144
145/**
146 * Set the microphone volume.
147 * @param {number} volume - The volume level to set.
148 */
149function setMic(volume) {
150 if (volume === undefined) return;
151 Uebersicht.run(`osascript -e 'set volume input volume ${volume}'`);
152}