All files / src/providers reaction.tsx

100% Statements 56/56
90.9% Branches 10/11
100% Functions 2/2
100% Lines 56/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 861x 1x 1x     1x     1x           1x           1x 1x 1x   1x 6x   6x 2x 2x   4x 4x   1x 4x 4x 4x     4x 4x   4x   3x 3x 3x 3x 1x 1x 1x 1x   1x   1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x     3x 3x 3x 4x   4x   4x 4x 4x 4x   4x  
import { createContext, useContext, useEffect, useState } from 'react';
import { Effect, Stream, Fiber } from 'effect';
import { toast } from 'sonner';
 
// Services
import { subscribeService } from '@/services/subscription';
 
// Stores
import { reactionStore } from '@/stores/reaction';
 
// Types
import { WSReactionData } from '@/types/meal';
 
// Constants
import { MESSAGES } from '@/constants';
 
interface ReactionContextValue {
  data: WSReactionData;
}
 
const ReactionContext = createContext<ReactionContextValue | undefined>(
  undefined,
);
 
export const useReaction = () => {
  const context = useContext(ReactionContext);
 
  if (!context) {
    throw new Error(MESSAGES.OPTION_DATA_CONTEXT_ERROR);
  }
 
  return context;
};
 
export const ReactionProvider = ({
  children,
  currentOrigin,
}: {
  children: React.ReactNode;
  currentOrigin?: string;
}) => {
  const [data, setData] = useState<WSReactionData>({});
 
  useEffect(() => {
    // Run subscription as an Effect fiber
    const fiber = Effect.runFork(
      Effect.scoped(
        subscribeService(currentOrigin).pipe(
          Effect.flatMap((stream) =>
            stream.pipe(
              Stream.tap((reaction) =>
                Effect.sync(() => {
                  if (!reaction) return;
 
                  setData((prev) => ({ ...prev, ...reaction }));
 
                  const entry = Object.entries(reaction)[0];
                  if (entry) {
                    const [id, stats] = entry;
                    reactionStore.setData(id, stats);
                  }
                }),
              ),
              Stream.runDrain, // consume the stream
            ),
          ),
          Effect.catchAll((err) => Effect.sync(() => toast.error(err.message))),
        ),
      ),
    );
 
    // Cleanup: interrupt fiber on unmount
    return () => {
      Fiber.interrupt(fiber);
    };
  }, [currentOrigin]);
 
  const contextValue: ReactionContextValue = { data };
 
  return (
    <ReactionContext.Provider value={contextValue}>
      {children}
    </ReactionContext.Provider>
  );
};