All files / frontend/src/contexts SnackbarContext.tsx

86.66% Statements 26/30
87.5% Branches 7/8
88.88% Functions 8/9
85.71% Lines 24/28

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 86 87 88 89 90 91 92 93                            5x             41x 41x 41x 41x   41x       8x             8x     41x           41x 41x 8x 8x 8x 8x       41x 41x             41x 41x             41x             41x               124x 124x     124x    
import { createContext, useContext, useState, useCallback, useEffect, ReactNode, useRef } from 'react';
import { SnackMessage, uiServiceSchemas } from '../../types';
import { validateServiceResponse } from '../utils/validation';
 
interface SnackbarContextValue {
  currentSnackMessage: SnackMessage | undefined;
  previousSnackMessage: SnackMessage | undefined;
  addSnackMessage: (
    message: string,
    options: Omit<SnackMessage, 'message'>
  ) => void;
  reportSnackDismiss: () => void;
}
 
const SnackbarContext = createContext<SnackbarContextValue | null>(null);
 
interface SnackbarProviderProps {
  children: ReactNode;
}
 
export function SnackbarProvider({ children }: SnackbarProviderProps) {
  const [queue, setQueue] = useState<SnackMessage[]>([]);
  const [current, setCurrent] = useState<SnackMessage | undefined>(undefined);
  const [previous, setPrevious] = useState<SnackMessage | undefined>(undefined);
  const processingRef = useRef(false);
 
  const addSnackMessage = useCallback((
    message: string,
    { severity, action, onActionClick, autoHideDuration = 3000 }: Omit<SnackMessage, 'message'>
  ) => {
    const snackMessage: SnackMessage = {
      message,
      severity,
      action,
      onActionClick,
      autoHideDuration
    };
    setQueue(prev => [...prev, snackMessage]);
  }, []);
 
  const reportSnackDismiss = useCallback(() => {
    setPrevious(current);
    setCurrent(undefined);
    processingRef.current = false;
  }, [current]);
 
  useEffect(() => {
    if (!current && queue.length > 0 && !processingRef.current) {
      processingRef.current = true;
      const nextMessage = queue[0];
      setQueue(prev => prev.slice(1));
      setCurrent(nextMessage);
    }
  }, [current, queue]);
 
  const getCurrentSnackMessage = useCallback(() => {
    return validateServiceResponse(
      'getCurrentSnackMessage',
      uiServiceSchemas.getCurrentSnackMessage,
      current
    );
  }, [current]);
 
  const getPreviousSnackMessage = useCallback(() => {
    return validateServiceResponse(
      'getPreviousSnackMessage',
      uiServiceSchemas.getPreviousSnackMessage,
      previous
    );
  }, [previous]);
 
  const value: SnackbarContextValue = {
    currentSnackMessage: getCurrentSnackMessage(),
    previousSnackMessage: getPreviousSnackMessage(),
    addSnackMessage,
    reportSnackDismiss
  };
 
  return (
    <SnackbarContext.Provider value={value}>
      {children}
    </SnackbarContext.Provider>
  );
}
 
export function useSnackbar(): SnackbarContextValue {
  const context = useContext(SnackbarContext);
  Iif (!context) {
    throw new Error('useSnackbar must be used within a SnackbarProvider');
  }
  return context;
}