- Campaigns: new table, CRUD API, React context + provider
- Sessions: scoped to campaigns, paths under campaigns/{id}/
- File browser: scoped per campaign, removed copy/paste/autoPlay
- Sidebar: campaign selector dropdown at top
- Transcribe: GPU model cached/released via job counter
- Jobs: status text updates dynamically in real-time
- Auto-redirect: blocked when summarize job is active
60 lines
1.8 KiB
TypeScript
60 lines
1.8 KiB
TypeScript
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react';
|
|
import { campaignApi, type Campaign } from '../api';
|
|
|
|
interface CampaignContextType {
|
|
currentCampaign: Campaign | null;
|
|
setCampaign: (c: Campaign | null) => void;
|
|
campaigns: Campaign[];
|
|
refreshCampaigns: () => void;
|
|
}
|
|
|
|
const CampaignContext = createContext<CampaignContextType | null>(null);
|
|
|
|
export function CampaignProvider({ children }: { children: ReactNode }) {
|
|
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
|
|
const [currentCampaign, setCurrentCampaign] = useState<Campaign | null>(null);
|
|
|
|
const refreshCampaigns = useCallback(async () => {
|
|
try {
|
|
const list = await campaignApi.list();
|
|
setCampaigns(list);
|
|
} catch {}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
refreshCampaigns();
|
|
}, [refreshCampaigns]);
|
|
|
|
useEffect(() => {
|
|
if (campaigns.length === 0) return;
|
|
if (!currentCampaign) {
|
|
const stored = localStorage.getItem('campaignId');
|
|
const match = stored ? campaigns.find(c => c.id === stored) : null;
|
|
setCurrentCampaign(match || campaigns[0]);
|
|
} else {
|
|
const stillExists = campaigns.find(c => c.id === currentCampaign.id);
|
|
if (!stillExists) {
|
|
setCurrentCampaign(campaigns[0] || null);
|
|
}
|
|
}
|
|
}, [campaigns]);
|
|
|
|
const setCampaign = (c: Campaign | null) => {
|
|
if (c) localStorage.setItem('campaignId', c.id);
|
|
else localStorage.removeItem('campaignId');
|
|
setCurrentCampaign(c);
|
|
};
|
|
|
|
return (
|
|
<CampaignContext.Provider value={{ currentCampaign, setCampaign, campaigns, refreshCampaigns }}>
|
|
{children}
|
|
</CampaignContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useCampaign() {
|
|
const ctx = useContext(CampaignContext);
|
|
if (!ctx) throw new Error('useCampaign must be used within CampaignProvider');
|
|
return ctx;
|
|
}
|