import { useMutation, useQueryClient } from '@tanstack/react-query'; import { createParticle, createStreamParticle, } from '@/lib/firestore-particles'; import { CONTAINER_TYPES, type NetworkUsage, type ParticleType, type ParticlePropertiesMap, } from '@/api/types'; import { parseParticlePath, particlePath, ParticlePath, toFirestoreChildrenPath, } from '@/lib/particle-path'; import { QuotaExceededError } from '@/lib/errors'; import { isUsageExhausted, networkUsageQueryKey, useBumpNetworkUsage, useInvalidateNetworkUsage, } from './use-network-usage'; interface CreateParticleParams { // Path to which the new particle will be added as a child path: ParticlePath; type: T; properties: ParticlePropertiesMap[T]; createdByHumanId: string; // Required for container types visibleTo?: string[]; } export function useCreateParticle() { const qc = useQueryClient(); const bumpUsage = useBumpNetworkUsage(); const invalidateUsage = useInvalidateNetworkUsage(); return useMutation({ // Compose UI renders a custom quota-exceeded toast + cancels the overlay. // Opt out of the global mutation error toast to avoid a double-toast. meta: { suppressToast: true }, mutationFn: async (params: CreateParticleParams) => { const { networkId } = parseParticlePath(params.path); // Containers aren't counted server-side, so we block them here if (!CONTAINER_TYPES.has(params.type)) { const cached = qc.getQueryData( networkUsageQueryKey(networkId), ); if (isUsageExhausted(cached)) { throw new QuotaExceededError(networkId); } } const collectionPath = toFirestoreChildrenPath(params.path); const result = await createParticle( collectionPath, params.type, params.properties, params.createdByHumanId, params.visibleTo, ); if (!CONTAINER_TYPES.has(params.type)) { bumpUsage(networkId); void invalidateUsage(networkId); } return result; }, }); } type CreateStreamParticleParams = { networkId: string; properties: ParticlePropertiesMap['stream']; createdByHumanId: string; visibleTo?: string[]; // Container to create the stream in; defaults to the network root. parentPath?: ParticlePath; }; export function useCreateStreamParticle() { return useMutation({ mutationFn: async (params: CreateStreamParticleParams) => { const path = params.parentPath ?? particlePath(params.networkId, []); const collectionPath = toFirestoreChildrenPath(path); return await createStreamParticle( collectionPath, params.properties, params.createdByHumanId, params.visibleTo, ); }, }); }