diff --git a/web/liveman/components/login.tsx b/web/liveman/components/login.tsx index e152738..cf4c032 100644 --- a/web/liveman/components/login.tsx +++ b/web/liveman/components/login.tsx @@ -26,7 +26,7 @@ function useInput(label: string, type = 'text') { export interface LoginProps { show: boolean; - onSuccess?: (token: string) => void; + onSuccess?: (tokenType: string, tokenValue: string) => void; } export function Login({ show, onSuccess }: LoginProps) { @@ -76,7 +76,7 @@ export function Login({ show, onSuccess }: LoginProps) { const tk = `${tokenType} ${tokenValue}`; livemanApi.setAuthToken(tk); sharedApi.setAuthToken(tk); - onSuccess?.(tokenValue); + onSuccess?.(tokenType, tokenValue); } catch (e) { livemanApi.setAuthToken(''); sharedApi.setAuthToken(''); diff --git a/web/liveman/components/nodes-table.tsx b/web/liveman/components/nodes-table.tsx index 9a985fb..8356603 100644 --- a/web/liveman/components/nodes-table.tsx +++ b/web/liveman/components/nodes-table.tsx @@ -18,9 +18,7 @@ export function NodesTable() { const tokenContext = useContext(TokenContext); useEffect(() => { - if (tokenContext.token.length > 0) { - nodes.updateData(); - } + nodes.updateData(); }, [tokenContext.token]); return ( @@ -57,7 +55,7 @@ export function NodesTable() { {n.status} {n.duration} - @@ -95,3 +92,12 @@ function NodeStrategyLabel({ strategy }: NodeStrategyLabelProps) { ); } + +function NodeLink({ node }: { node: Node }) { + const urlObject = new URL(location.href); + urlObject.searchParams.set('nodes', node.alias); + const url = urlObject.toString(); + return ( + {url} + ); +} diff --git a/web/liveman/liveman.tsx b/web/liveman/liveman.tsx index 7cc4731..157bf59 100644 --- a/web/liveman/liveman.tsx +++ b/web/liveman/liveman.tsx @@ -1,25 +1,33 @@ import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; import { Button } from 'react-daisyui'; -import { type Stream } from '@/shared/api'; import { useNeedAuthorization } from '@/shared/hooks/use-need-authorization'; import { StreamsTable } from '@/shared/components/streams-table'; import { PageLayout } from '@/shared/components/page-layout'; import * as api from './api'; -import { Login } from './components/login'; +import { type LoginProps, Login } from './components/login'; import { NodesTable } from './components/nodes-table'; import { type IStreamTokenDialog, StreamTokenDialog } from './components/dialog-token'; +const TOKEN_KEY = 'liveman_auth_token'; +const initialToken = localStorage.getItem(TOKEN_KEY) ?? ''; +if (initialToken) { + api.setAuthToken(initialToken); +} + +const initialNodes = new URLSearchParams(location.search).getAll('nodes'); + export function Liveman() { - const [token, setToken] = useState(''); + const [token, setToken] = useState(initialToken); const [needsAuthorizaiton, setNeedsAuthorization] = useNeedAuthorization(api); - const onLoginSuccess = (t: string) => { - setToken(t); + const onLoginSuccess: LoginProps['onSuccess'] = (tokenType, tokenValue) => { + setToken(tokenValue); setNeedsAuthorization(false); + localStorage.setItem(TOKEN_KEY, `${tokenType} ${tokenValue}`); }; - const [filterNodes, setFilterNodes] = useState(); + const [filterNodes, setFilterNodes] = useState(initialNodes); useEffect(() => { const params = new URLSearchParams(location.search); setFilterNodes(params.getAll('nodes')); @@ -27,10 +35,10 @@ export function Liveman() { const getStreams = useCallback(async () => { const streams = await api.getStreams(filterNodes); return streams.sort((a, b) => a.createdAt - b.createdAt); - }, [filterNodes]); + }, filterNodes); const getWhxpUrl = (whxp: 'whep' | 'whip', streamId: string) => { let url = `/${whxp}/${streamId}`; - if (filterNodes && filterNodes.length > 0) { + if (filterNodes.length > 0) { const params = new URLSearchParams(); filterNodes?.forEach(v => params.append('nodes', v)); url += `?${params.toString()}`; @@ -39,21 +47,18 @@ export function Liveman() { }; const refStreamTokenDialog = useRef(null); - const renderCreateToken = useCallback((stream: Stream) => { - return ( - - ); - }, []); return ( <> - {filterNodes && filterNodes.length > 0 ? null : } + {filterNodes.length > 0 ? null : } getWhxpUrl('whep', streamId)} getWhipUrl={streamId => getWhxpUrl('whip', streamId)} - renderExtraActions={renderCreateToken} + renderExtraActions={stream => ( + + )} /> diff --git a/web/shared/components/streams-table.tsx b/web/shared/components/streams-table.tsx index ea971f6..40e95b6 100644 --- a/web/shared/components/streams-table.tsx +++ b/web/shared/components/streams-table.tsx @@ -44,9 +44,7 @@ export function StreamsTable(props: StreamTableProps) { const tokenContext = useContext(TokenContext); useEffect(() => { - if (tokenContext.token.length > 0) { - streams.updateData(); - } + streams.updateData(); }, [tokenContext.token]); const handleViewClients = (id: string) => { diff --git a/web/shared/hooks/use-refresh-timer.ts b/web/shared/hooks/use-refresh-timer.ts index e3518bd..9ce1f0a 100644 --- a/web/shared/hooks/use-refresh-timer.ts +++ b/web/shared/hooks/use-refresh-timer.ts @@ -1,16 +1,12 @@ import { useCallback, useEffect, useState } from 'preact/hooks'; -export function useRefreshTimer(initial: T, fetchData: () => Promise, timeout = 3000, immediate = true) { +export function useRefreshTimer(initial: T, fetchData: () => Promise, timeout = 3000) { const [data, setData] = useState(initial); - const [isImmediate, _setIsImmediate] = useState(immediate); const [refreshTimer, setRefreshTimer] = useState(-1); const isRefreshing = refreshTimer > 0; + const updateData = useCallback(async () => setData(await fetchData()), [fetchData]); - useEffect(() => { - if (isImmediate) { - updateData(); - } - }, []); + useEffect(() => { if (isRefreshing) { window.clearInterval(refreshTimer); @@ -20,6 +16,7 @@ export function useRefreshTimer(initial: T, fetchData: () => Promise, time window.clearInterval(refreshTimer); }; }, [updateData, timeout]); + const toggleTimer = () => { if (isRefreshing) { clearInterval(refreshTimer); @@ -29,6 +26,7 @@ export function useRefreshTimer(initial: T, fetchData: () => Promise, time setRefreshTimer(window.setInterval(updateData, timeout)); } }; + return { data, isRefreshing,