Feat : Link streamers and recorders
This commit is contained in:
@@ -251,6 +251,7 @@ export function App() {
|
||||
{openAgent && (
|
||||
<AgentSettings
|
||||
agent={openAgent}
|
||||
targets={targets}
|
||||
onClose={() => setSettingsFor(null)}
|
||||
onCommand={runCommand}
|
||||
notify={notify}
|
||||
|
||||
@@ -3,18 +3,29 @@ import type {
|
||||
AgentAction,
|
||||
AgentView,
|
||||
BrowserSettings,
|
||||
StreamState,
|
||||
WatchSettings,
|
||||
WatchTarget,
|
||||
} from '@stream-control/shared';
|
||||
import { api } from '../api';
|
||||
|
||||
interface Props {
|
||||
agent: AgentView;
|
||||
/** Streamers déjà suivis : ils alimentent la sélection ci-dessous. */
|
||||
targets: WatchTarget[];
|
||||
onClose: () => void;
|
||||
onCommand: (id: string, action: AgentAction, params?: Record<string, unknown>) => Promise<void>;
|
||||
notify: (message: string, tone?: 'info' | 'error') => void;
|
||||
}
|
||||
|
||||
export function AgentSettings({ agent, onClose, onCommand, notify }: Props) {
|
||||
const STATE_HINTS: Record<StreamState, string> = {
|
||||
public: 'en direct',
|
||||
private: 'show privé',
|
||||
offline: 'hors-ligne',
|
||||
unknown: '',
|
||||
};
|
||||
|
||||
export function AgentSettings({ agent, targets, onClose, onCommand, notify }: Props) {
|
||||
const [name, setName] = useState(agent.name);
|
||||
const [host, setHost] = useState(agent.obs.host);
|
||||
const [port, setPort] = useState(String(agent.obs.port));
|
||||
@@ -27,6 +38,12 @@ export function AgentSettings({ agent, onClose, onCommand, notify }: Props) {
|
||||
const [watch, setWatch] = useState<WatchSettings>(agent.watch);
|
||||
const [browser, setBrowser] = useState<BrowserSettings>(agent.browser);
|
||||
|
||||
// Saisie libre : soit l'opérateur la demande, soit le pseudo déjà configuré
|
||||
// ne fait pas partie des streamers suivis.
|
||||
const [manualUsername, setManualUsername] = useState(
|
||||
() => Boolean(agent.watch.username) && !targets.some((t) => t.username === agent.watch.username),
|
||||
);
|
||||
|
||||
function patchBrowser(patch: Partial<BrowserSettings>) {
|
||||
setBrowser((current) => ({ ...current, ...patch }));
|
||||
}
|
||||
@@ -241,12 +258,56 @@ export function AgentSettings({ agent, onClose, onCommand, notify }: Props) {
|
||||
</select>
|
||||
</label>
|
||||
<label className="field grow">
|
||||
<span>Pseudo du streamer</span>
|
||||
<input
|
||||
value={watch.username}
|
||||
onChange={(event) => patchWatch({ username: event.target.value })}
|
||||
placeholder="tel qu'il apparaît dans l'URL"
|
||||
/>
|
||||
<span>Streamer surveillé</span>
|
||||
{targets.length > 0 && !manualUsername ? (
|
||||
<select
|
||||
value={watch.username}
|
||||
onChange={(event) => {
|
||||
if (event.target.value === '__manual__') {
|
||||
setManualUsername(true);
|
||||
patchWatch({ username: '' });
|
||||
} else {
|
||||
patchWatch({ username: event.target.value });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<option value="">— aucun —</option>
|
||||
{targets.map((target) => {
|
||||
const hint = STATE_HINTS[target.state];
|
||||
return (
|
||||
<option key={target.id} value={target.username}>
|
||||
{target.label ?? target.username}
|
||||
{hint ? ` — ${hint}` : ''}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
<option value="__manual__">Autre pseudo…</option>
|
||||
</select>
|
||||
) : (
|
||||
<input
|
||||
value={watch.username}
|
||||
onChange={(event) => patchWatch({ username: event.target.value })}
|
||||
placeholder="tel qu'il apparaît dans l'URL"
|
||||
autoFocus={manualUsername}
|
||||
/>
|
||||
)}
|
||||
{targets.length === 0 ? (
|
||||
<span className="muted small">
|
||||
Aucun streamer suivi — ajoute-le dans l'onglet Streamers pour le
|
||||
retrouver ici.
|
||||
</span>
|
||||
) : manualUsername ? (
|
||||
<button
|
||||
type="button"
|
||||
className="link-button"
|
||||
onClick={() => {
|
||||
setManualUsername(false);
|
||||
patchWatch({ username: '' });
|
||||
}}
|
||||
>
|
||||
← revenir à la liste
|
||||
</button>
|
||||
) : null}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -380,6 +380,19 @@ fieldset.group {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.link-button {
|
||||
align-self: flex-start;
|
||||
padding: 2px 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--accent);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.link-button:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
fieldset.group > legend {
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
|
||||
Reference in New Issue
Block a user