import * as Uebersicht from "uebersicht"; import * as DataWidget from "./data-widget.jsx"; import * as DataWidgetLoader from "./data-widget-loader.jsx"; import * as Icons from "../icons/icons.jsx"; import { SuspenseIcon } from "../icons/icon.jsx"; import useWidgetRefresh from "../../hooks/use-widget-refresh"; import useServerSocket from "../../hooks/use-server-socket"; import * as Utils from "../../utils"; import { useSimpleBarContext } from "../simple-bar-context.jsx"; const { React } = Uebersicht; export { micStyles as styles } from "../../styles/components/data/mic"; const DEFAULT_REFRESH_FREQUENCY = 20000; /** * Mic widget component. * @returns {JSX.Element} The rendered mic widget. */ export const Widget = React.memo(() => { const { displayIndex, settings } = useSimpleBarContext(); const { widgets, micWidgetOptions } = settings; const { micWidget } = widgets; const { refreshFrequency, showOnDisplay, showIcon } = micWidgetOptions; // Determine the refresh frequency for the widget. const refresh = React.useMemo( () => Utils.getRefreshFrequency(refreshFrequency, DEFAULT_REFRESH_FREQUENCY), [refreshFrequency], ); // Determine if the widget should be visible. const visible = Utils.isVisibleOnDisplay(displayIndex, showOnDisplay) && micWidget; const [state, setState] = React.useState(); const [loading, setLoading] = React.useState(visible); const { volume: _volume } = state || {}; const [volume, setVolume] = React.useState(_volume && parseInt(_volume, 10)); const [dragging, setDragging] = React.useState(false); /** * Reset the widget state. */ const resetWidget = () => { setState(undefined); setLoading(false); }; /** * Fetch the current microphone volume. */ const getMic = React.useCallback(async () => { if (!visible) return; const volume = await Utils.cachedRun( `osascript -e 'input volume of (get volume settings)'`, refresh, ); setState({ volume: Utils.cleanupOutput(volume) }); setLoading(false); }, [visible, refresh]); // Use server socket to get mic data. useServerSocket("mic", visible, getMic, resetWidget, setLoading); // Refresh the widget periodically. useWidgetRefresh(visible, getMic, refresh); // Update the mic volume when dragging state changes. React.useEffect(() => { if (!dragging) setMic(volume); }, [dragging, volume]); // Update the volume state when the fetched volume changes. React.useEffect(() => { setVolume((currentVolume) => { if (_volume && currentVolume !== parseInt(_volume, 10)) { return parseInt(_volume, 10); } return currentVolume; }); }, [_volume]); if (loading) return ; if (!state || volume === undefined || _volume === "missing value") return null; const Icon = !volume ? Icons.MicOff : Icons.MicOn; /** * Handle volume change event. * @param {React.ChangeEvent} e - The change event. */ const onChange = (e) => { const value = parseInt(e.target.value, 10); setVolume(value); }; /** * Handle mouse down event on the slider. */ const onMouseDown = () => setDragging(true); /** * Handle mouse up event on the slider. */ const onMouseUp = () => setDragging(false); const formattedVolume = `${volume.toString().padStart(2, "0")}%`; const classes = Utils.classNames("mic", { "mic--dragging": dragging, }); return (
{showIcon && ( )} {formattedVolume}
); }); Widget.displayName = "Mic"; /** * Set the microphone volume. * @param {number} volume - The volume level to set. */ function setMic(volume) { if (volume === undefined) return; Uebersicht.run(`osascript -e 'set volume input volume ${volume}'`); }