mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[hAIk]: integrate subscriptions backend-bridge into frontend: replace all raw fetch calls to /subscriptions/* endpoints with dispatch(THUNK).unwrap() using SUBSCRIPTIONS_STATUS, SUBSCRIPTIONS_CONNECT, SUBSCRIPTIONS_POLL, and SUBSCRIPTIONS_DISCONNECT from the bridge layer — no Redux slice needed since state stays local
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { API_BASE } from '@/shared/config';
|
||||
import { useAppDispatch } from '@/shared/hooks';
|
||||
import { SUBSCRIPTIONS_STATUS } from '@/shared/backend-bridge/apps/subscriptions';
|
||||
import { ToolIntegration } from './onboardingConstants';
|
||||
import { useSubscriptionConnect } from './useSubscriptionConnect';
|
||||
|
||||
export function useOnboarding() {
|
||||
const dispatch = useAppDispatch();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [step, setStep] = useState<'provider' | 'tools'>('provider');
|
||||
const [connecting, setConnecting] = useState<string | null>(null);
|
||||
@@ -27,11 +30,10 @@ export function useOnboarding() {
|
||||
let attempts = 0;
|
||||
const maxAttempts = 15;
|
||||
const check = () => {
|
||||
fetch(`${API_BASE}/subscriptions/status`)
|
||||
.then((r) => r.json())
|
||||
dispatch(SUBSCRIPTIONS_STATUS()).unwrap()
|
||||
.then((data) => {
|
||||
if (data.running) {
|
||||
const connections = data.providers?.connections || [];
|
||||
const connections = (data.providers as any)?.connections || [];
|
||||
if (connections.some((p: any) => p.isActive)) return;
|
||||
setTimeout(() => setNineRouterReady(true), 3000);
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { useCallback, MutableRefObject } from 'react';
|
||||
import { API_BASE } from '@/shared/config';
|
||||
import { useAppDispatch } from '@/shared/hooks';
|
||||
import {
|
||||
SUBSCRIPTIONS_CONNECT,
|
||||
SUBSCRIPTIONS_POLL,
|
||||
SUBSCRIPTIONS_STATUS,
|
||||
} from '@/shared/backend-bridge/apps/subscriptions';
|
||||
|
||||
interface UseSubscriptionConnectParams {
|
||||
pollTimerRef: MutableRefObject<any>;
|
||||
@@ -11,6 +16,8 @@ interface UseSubscriptionConnectParams {
|
||||
export function useSubscriptionConnect({
|
||||
pollTimerRef, msgHandlerRef, setConnecting, advanceToTools,
|
||||
}: UseSubscriptionConnectParams) {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleConnect = useCallback(async (providerId: string) => {
|
||||
if (pollTimerRef.current) { clearInterval(pollTimerRef.current); pollTimerRef.current = null; }
|
||||
if (msgHandlerRef.current) { window.removeEventListener('message', msgHandlerRef.current); msgHandlerRef.current = null; }
|
||||
@@ -19,34 +26,20 @@ export function useSubscriptionConnect({
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
|
||||
try {
|
||||
const r = await fetch(`${API_BASE}/subscriptions/connect`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ provider: providerId }),
|
||||
});
|
||||
if (!r.ok) {
|
||||
setConnecting(null);
|
||||
return;
|
||||
}
|
||||
const data = await r.json();
|
||||
const data = await dispatch(SUBSCRIPTIONS_CONNECT(providerId)).unwrap();
|
||||
|
||||
if (data.flow === 'device_code') {
|
||||
if (data.verification_uri) window.open(data.verification_uri, '_blank');
|
||||
if (data.verification_uri) window.open(data.verification_uri as string, '_blank');
|
||||
|
||||
const timer = setInterval(async () => {
|
||||
try {
|
||||
const pr = await fetch(`${API_BASE}/subscriptions/poll`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
provider: providerId,
|
||||
device_code: data.device_code,
|
||||
code_verifier: data.code_verifier,
|
||||
extra_data: data.extra_data,
|
||||
}),
|
||||
});
|
||||
const pd = await pr.json();
|
||||
if (pd.success) {
|
||||
const pd = await dispatch(SUBSCRIPTIONS_POLL({
|
||||
provider: providerId,
|
||||
device_code: data.device_code as string,
|
||||
code_verifier: data.code_verifier as string | undefined,
|
||||
extra_data: data.extra_data as Record<string, unknown> | undefined,
|
||||
})).unwrap();
|
||||
if ((pd as any).success) {
|
||||
clearInterval(timer);
|
||||
pollTimerRef.current = null;
|
||||
advanceToTools();
|
||||
@@ -57,7 +50,7 @@ export function useSubscriptionConnect({
|
||||
setTimeout(() => { clearInterval(timer); pollTimerRef.current = null; setConnecting(null); }, 30000);
|
||||
|
||||
} else if (data.flow === 'authorization_code') {
|
||||
const popup = window.open(data.auth_url, 'oauth_connect', 'width=600,height=700');
|
||||
const popup = window.open(data.auth_url as string, 'oauth_connect', 'width=600,height=700');
|
||||
let resolved = false;
|
||||
const cleanup = () => {
|
||||
if (resolved) return;
|
||||
@@ -87,9 +80,8 @@ export function useSubscriptionConnect({
|
||||
advanceToTools();
|
||||
return;
|
||||
}
|
||||
const sr = await fetch(`${API_BASE}/subscriptions/status`);
|
||||
const sd = await sr.json();
|
||||
const connections = sd.providers?.connections || [];
|
||||
const sd = await dispatch(SUBSCRIPTIONS_STATUS()).unwrap();
|
||||
const connections = (sd.providers as any)?.connections || [];
|
||||
if (connections.some((p: any) => p.provider === providerId && p.isActive)) {
|
||||
cleanup();
|
||||
advanceToTools();
|
||||
@@ -105,7 +97,7 @@ export function useSubscriptionConnect({
|
||||
} catch {
|
||||
setConnecting(null);
|
||||
}
|
||||
}, [pollTimerRef, msgHandlerRef, setConnecting, advanceToTools]);
|
||||
}, [dispatch, pollTimerRef, msgHandlerRef, setConnecting, advanceToTools]);
|
||||
|
||||
return handleConnect;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Box, Typography, CircularProgress } from '@mui/material';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import { API_BASE } from '@/shared/config';
|
||||
import { useAppDispatch } from '@/shared/hooks';
|
||||
import {
|
||||
SUBSCRIPTIONS_STATUS,
|
||||
SUBSCRIPTIONS_CONNECT,
|
||||
SUBSCRIPTIONS_POLL,
|
||||
SUBSCRIPTIONS_DISCONNECT,
|
||||
} from '@/shared/backend-bridge/apps/subscriptions';
|
||||
import SubscriptionCard, { SUBSCRIPTION_PROVIDERS } from './SubscriptionCard';
|
||||
|
||||
const SubscriptionCards: React.FC = () => {
|
||||
const c = useClaudeTokens();
|
||||
const dispatch = useAppDispatch();
|
||||
const [status, setStatus] = useState<any>(null);
|
||||
const [connecting, setConnecting] = useState<string | null>(null);
|
||||
const [disconnecting, setDisconnecting] = useState<string | null>(null);
|
||||
@@ -13,8 +20,7 @@ const SubscriptionCards: React.FC = () => {
|
||||
const [pollTimer, setPollTimer] = useState<any>(null);
|
||||
const retryRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
const fetchStatus = () => {
|
||||
fetch(`${API_BASE}/subscriptions/status`)
|
||||
.then(r => r.json())
|
||||
dispatch(SUBSCRIPTIONS_STATUS()).unwrap()
|
||||
.then(setStatus)
|
||||
.catch(() => setStatus({ running: false, providers: [], models: [] }));
|
||||
};
|
||||
@@ -45,24 +51,20 @@ const SubscriptionCards: React.FC = () => {
|
||||
setUserCode('');
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
try {
|
||||
const r = await fetch(`${API_BASE}/subscriptions/connect`, {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ provider: providerId }),
|
||||
});
|
||||
if (!r.ok) { setConnecting(null); return; }
|
||||
const data = await r.json();
|
||||
const data = await dispatch(SUBSCRIPTIONS_CONNECT(providerId)).unwrap();
|
||||
if (data.flow === 'device_code') {
|
||||
const code = data.user_code || '';
|
||||
const code = (data.user_code as string) || '';
|
||||
setUserCode(code);
|
||||
if (data.verification_uri) window.open(data.verification_uri, '_blank');
|
||||
if (data.verification_uri) window.open(data.verification_uri as string, '_blank');
|
||||
const timer = setInterval(async () => {
|
||||
try {
|
||||
const pr = await fetch(`${API_BASE}/subscriptions/poll`, {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ provider: providerId, device_code: data.device_code, code_verifier: data.code_verifier, extra_data: data.extra_data }),
|
||||
});
|
||||
const pd = await pr.json();
|
||||
if (pd.success) {
|
||||
const pd = await dispatch(SUBSCRIPTIONS_POLL({
|
||||
provider: providerId,
|
||||
device_code: data.device_code as string,
|
||||
code_verifier: data.code_verifier as string | undefined,
|
||||
extra_data: data.extra_data as Record<string, unknown> | undefined,
|
||||
})).unwrap();
|
||||
if ((pd as any).success) {
|
||||
clearInterval(timer);
|
||||
setPollTimer(null);
|
||||
setConnecting(null);
|
||||
@@ -74,7 +76,7 @@ const SubscriptionCards: React.FC = () => {
|
||||
setPollTimer(timer);
|
||||
setTimeout(() => { clearInterval(timer); setPollTimer(null); setConnecting(null); setUserCode(''); }, 300000);
|
||||
} else if (data.flow === 'authorization_code') {
|
||||
const popup = window.open(data.auth_url, 'oauth_connect', 'width=600,height=700');
|
||||
const popup = window.open(data.auth_url as string, 'oauth_connect', 'width=600,height=700');
|
||||
let resolved = false;
|
||||
const cleanup = () => {
|
||||
if (resolved) return;
|
||||
@@ -98,9 +100,8 @@ const SubscriptionCards: React.FC = () => {
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
const sr = await fetch(`${API_BASE}/subscriptions/status`);
|
||||
const sd = await sr.json();
|
||||
const connections = sd.providers?.connections || [];
|
||||
const sd = await dispatch(SUBSCRIPTIONS_STATUS()).unwrap();
|
||||
const connections = (sd.providers as any)?.connections || [];
|
||||
if (connections.some((p: any) => p.provider === providerId && p.isActive)) {
|
||||
cleanup();
|
||||
}
|
||||
@@ -116,11 +117,7 @@ const SubscriptionCards: React.FC = () => {
|
||||
const handleDisconnect = async (providerId: string) => {
|
||||
setDisconnecting(providerId);
|
||||
try {
|
||||
await fetch(`${API_BASE}/subscriptions/disconnect`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ provider: providerId }),
|
||||
});
|
||||
await dispatch(SUBSCRIPTIONS_DISCONNECT(providerId)).unwrap();
|
||||
} catch {}
|
||||
setTimeout(() => { fetchStatusWithRetry(); setDisconnecting(null); }, 500);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user