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 useWidgetRefresh from "../../hooks/use-widget-refresh";
import useServerSocket from "../../hooks/use-server-socket";
import { useSimpleBarContext } from "../simple-bar-context.jsx";
import * as Utils from "../../utils";
export { viscosityVPNStyles as styles } from "../../styles/components/data/viscosity-vpn";
const { React } = Uebersicht;
const DEFAULT_REFRESH_FREQUENCY = 8000;
/**
* Viscosity VPN Widget component.
* @returns {JSX.Element|null} The Viscosity VPN widget.
*/
export const Widget = React.memo(() => {
const { displayIndex, settings, pushMissive } = useSimpleBarContext();
const { widgets, vpnWidgetOptions } = settings;
const { vpnWidget } = widgets;
const {
refreshFrequency,
vpnConnectionName,
vpnShowConnectionName,
showOnDisplay,
showIcon,
} = vpnWidgetOptions;
// 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) && vpnWidget;
const [state, setState] = React.useState();
const [loading, setLoading] = React.useState(visible);
const [isViscosityActive, setIsViscosityActive] = React.useState(false);
/**
* Reset the widget state.
*/
const resetWidget = () => {
setState(undefined);
setLoading(false);
setIsViscosityActive(false);
};
/**
* Fetch the current VPN status.
*/
const getVPN = React.useCallback(async () => {
if (!visible) return;
const isRunning = await Utils.cachedRun(
`pgrep -xq 'Viscosity' && echo "true" || echo "false"`,
refresh,
);
if (Utils.cleanupOutput(isRunning) === "false") {
setLoading(false);
setIsViscosityActive(false);
return;
}
const status = await Utils.cachedRun(
`osascript -e "tell application \\"Viscosity\\" to return state of the first connection where name is equal to \\"${vpnConnectionName}\\"" 2>/dev/null`,
refresh,
);
if (!status.length) return;
setState({ status: Utils.cleanupOutput(status) });
setIsViscosityActive(true);
setLoading(false);
}, [visible, vpnConnectionName, refresh]);
// Use server socket to listen for VPN status updates
useServerSocket("viscosity-vpn", visible, getVPN, resetWidget, setLoading);
// Refresh the widget at the specified interval
useWidgetRefresh(visible, getVPN, refresh);
if (loading) return ;
if (!state || !vpnConnectionName.length || !isViscosityActive) return null;
const { status } = state;
const isConnected = status === "Connected";
const classes = Utils.classNames("viscosity-vpn", {
"viscosity-vpn--disconnected": !isConnected,
});
const Icon = isConnected ? Icons.VPN : Icons.VPNOff;
/**
* Handle click event to toggle VPN connection.
* @param {Event} e - The click event.
*/
const clicked = (e) => {
Utils.clickEffect(e);
toggleVPN(isConnected, vpnConnectionName, pushMissive);
setTimeout(getVPN, refreshFrequency / 2);
};
return (
{vpnShowConnectionName ? vpnConnectionName : status}
);
});
Widget.displayName = "ViscosityVPN";
/**
* Toggle the VPN connection.
* @param {boolean} isConnected - Whether the VPN is currently connected.
* @param {string} vpnConnectionName - The name of the VPN connection.
* @param {function} pushMissive - Function to push notifications.
*/
function toggleVPN(isConnected, vpnConnectionName, pushMissive) {
if (isConnected) {
Uebersicht.run(
`osascript -e 'tell application "Viscosity" to disconnect "${vpnConnectionName}"'`,
);
Utils.notification(
`Disabling Viscosity ${vpnConnectionName} network...`,
pushMissive,
);
} else {
Uebersicht.run(
`osascript -e 'tell application "Viscosity" to connect "${vpnConnectionName}"'`,
);
Utils.notification(
`Enabling Viscosity ${vpnConnectionName} network...`,
pushMissive,
);
}
}