From 0390a54fe2874672b88f1e5ceac288f40808240b Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Mon, 1 Jun 2026 23:03:42 +0000 Subject: [PATCH] feat(ui): add useP2PMode hook and embedded prop to Nodes and P2P pages Signed-off-by: Ettore Di Giacinto --- core/http/react-ui/src/hooks/useP2PMode.js | 31 ++++++++++++++++++++ core/http/react-ui/src/pages/Nodes.jsx | 24 ++++++++------- core/http/react-ui/src/pages/P2P.jsx | 34 ++++++++++++---------- 3 files changed, 62 insertions(+), 27 deletions(-) create mode 100644 core/http/react-ui/src/hooks/useP2PMode.js diff --git a/core/http/react-ui/src/hooks/useP2PMode.js b/core/http/react-ui/src/hooks/useP2PMode.js new file mode 100644 index 000000000..98676df9f --- /dev/null +++ b/core/http/react-ui/src/hooks/useP2PMode.js @@ -0,0 +1,31 @@ +import { useState, useEffect, useCallback } from 'react' +import { p2pApi } from '../utils/api' + +// useP2PMode reports whether p2p / swarm mode is available, mirroring +// useDistributedMode. Availability is "a network token exists" (the same signal +// the standalone P2P page used). One-shot probe on mount plus a manual refetch. +// +// Returns: +// enabled — true when a non-empty network token is present +// loading — true until the first probe completes +// refetch — manual trigger to re-run the probe +export function useP2PMode() { + const [enabled, setEnabled] = useState(false) + const [loading, setLoading] = useState(true) + + const probe = useCallback(async () => { + setLoading(true) + try { + const token = await p2pApi.getToken() + setEnabled(!!(token && String(token).trim())) + } catch { + setEnabled(false) + } finally { + setLoading(false) + } + }, []) + + useEffect(() => { probe() }, [probe]) + + return { enabled, loading, refetch: probe } +} diff --git a/core/http/react-ui/src/pages/Nodes.jsx b/core/http/react-ui/src/pages/Nodes.jsx index 769371854..b1da15d20 100644 --- a/core/http/react-ui/src/pages/Nodes.jsx +++ b/core/http/react-ui/src/pages/Nodes.jsx @@ -89,7 +89,7 @@ function WorkerHintCard({ addToast, activeTab, hasWorkers }) { ) } -export default function Nodes() { +export default function Nodes({ embedded = false }) { const { addToast } = useOutletContext() const { t } = useTranslation('admin') const [nodesList, setNodesList] = useState([]) @@ -282,16 +282,18 @@ export default function Nodes() { : activeTab === 'agent' ? agentNodes : backendNodes return ( -
- - - {t('nodes.title')} - - } - supporting={t('nodes.subtitle')} - /> +
+ {!embedded && ( + + + {t('nodes.title')} + + } + supporting={t('nodes.subtitle')} + /> + )} diff --git a/core/http/react-ui/src/pages/P2P.jsx b/core/http/react-ui/src/pages/P2P.jsx index 0da1e9651..cfad3ecef 100644 --- a/core/http/react-ui/src/pages/P2P.jsx +++ b/core/http/react-ui/src/pages/P2P.jsx @@ -69,7 +69,7 @@ function NoNodes({ icon, title, hint }) { ) } -export default function P2P() { +export default function P2P({ embedded = false }) { const { addToast } = useOutletContext() const { t } = useTranslation('admin') const [workers, setWorkers] = useState([]) @@ -145,7 +145,7 @@ export default function P2P() { if (loading) { return ( -
+
) @@ -154,7 +154,7 @@ export default function P2P() { // ── P2P Disabled ── if (!enabled) { return ( -
+

P2P distribution not enabled

@@ -243,19 +243,21 @@ export default function P2P() { const mlxTotal = stats.mlx_workers?.total ?? 0 return ( -
- {t('p2p.title')}} - supporting={ - <> - {t('p2p.subtitle')} - {' '} - -