From 0c8fa3a2a270747f3dbb54fe8792f3f0aec997e3 Mon Sep 17 00:00:00 2001
From: Ryan Schanzenbacher <ryan@rschanz.org>
Date: Mon, 3 Aug 2026 01:22:44 -0400
Subject: Initial commit: combined darwin + guix-linux home-manager flake

Supersedes nix-dotfiles (darwin) and guix-dotfiles' ad-hoc
nix-home-manager flake (linux). See README.md/MIGRATION.md.
---
 .../lib/components/data/viscosity-vpn.jsx          | 144 +++++++++++++++++++++
 1 file changed, 144 insertions(+)
 create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/viscosity-vpn.jsx

(limited to 'users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/viscosity-vpn.jsx')

diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/viscosity-vpn.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/viscosity-vpn.jsx
new file mode 100755
index 0000000..340c59b
--- /dev/null
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/viscosity-vpn.jsx
@@ -0,0 +1,144 @@
+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 <DataWidgetLoader.Widget className="viscosity-vpn" />;
+  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 (
+    <DataWidget.Widget
+      classes={classes}
+      Icon={showIcon ? Icon : null}
+      onClick={clicked}
+    >
+      {vpnShowConnectionName ? vpnConnectionName : status}
+    </DataWidget.Widget>
+  );
+});
+
+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,
+    );
+  }
+}
-- 
cgit v1.3.1

