WebJamApps/JaMmusic

View on GitHub
src/providers/Auth.provider.tsx

Summary

Maintainability
A
0 mins
Test Coverage
A
95%
import {
  createContext, MutableRefObject, useEffect, useRef, useState,
} from 'react';
import superagent from 'superagent';
import jwt from 'jwt-simple';

export const setInitValue = (
  name: string,
  setValue: React.Dispatch<React.SetStateAction<string>>,
  defaultValue: string,
) => {
  try {
    const storedValue = localStorage.getItem(name);
    if (typeof storedValue === 'string') setValue(storedValue);
    else localStorage.setItem(name, defaultValue);
  } catch {
    setValue(defaultValue);
  }
};

export const handleValueChange = (
  current: string,
  value: string,
) => {
  try {
    localStorage.setItem(current, value);
    return value;
  } catch (err) {
    const eMessage = (err as Error).message;
    console.log(eMessage); return eMessage;
  }
};

export const handleNameChange = (
  nameRef: MutableRefObject<string>,
  name: string,
  value: string,
) => {
  const lastName = nameRef.current;
  if (name !== lastName) {
    try {
      localStorage.setItem(name, value);
      nameRef.current = name;
      localStorage.removeItem(lastName);
      return name;
    } catch (err) {
      const eMessage = (err as Error).message;
      console.log(eMessage); return eMessage;
    }
  }
  return '';
};

const usePersistedState = (name: string, defaultValue: string) => {
  const initValue = localStorage.getItem(name);
  const [value, setValue] = useState(initValue || defaultValue);
  const nameRef = useRef(name);

  // eslint-disable-next-line react-hooks/exhaustive-deps
  useEffect(() => { setInitValue(name, setValue, defaultValue); }, []);

  useEffect(() => { handleValueChange(nameRef.current, value); }, [value]);

  // eslint-disable-next-line react-hooks/exhaustive-deps
  useEffect(() => { handleNameChange(nameRef, name, value); }, [name]);

  return [value, setValue];
};

export interface Iauth {
  isAuthenticated: boolean,
  error: string,
  token: string,
  user: {
    userType: string;
    email: string,
  };
}

export const defaultAuth: Iauth = {
  isAuthenticated: false,
  error: '',
  token: '',
  user: { userType: '', email: '' },
};

export const defaultSetAuth = (arg0: Iauth) => { console.log(arg0); };

export const AuthContext = createContext({
  auth: defaultAuth,
  setAuth: defaultSetAuth,
});

export const configAuth = (authString: string, setAuthString: (arg0: string) => void) => {
  let auth = defaultAuth, setAuth = defaultSetAuth;
  try {
    auth = JSON.parse(authString);
    const setter: (arg0: string) => void = setAuthString as (arg0: string) => void;
    setAuth = (arg0: Iauth) => { setter(JSON.stringify(arg0)); };
  } catch (err) { console.log((err as Error).message); }
  return { auth, setAuth };
};

export const setUserAuth = async (
  token: string,
  userId: string | undefined,
  setAuthType: (arg0: string | Iauth) => void,
  type: 'setAuth' | 'setAuthString',
) => {
  try {
    const { body } = await superagent.get(`${process.env.BackendUrl}/user/${userId}`)
      .set('Accept', 'application/json').set('Authorization', `Bearer ${token}`);
    if (type === 'setAuthString') {
      setAuthType(JSON.stringify({
        error: '', isAuthenticated: true, token, user: body,
      }));
    } else {
      setAuthType({
        error: '', isAuthenticated: true, token, user: body,
      });
    }
  } catch (err) { // if there is an error, reset auth to initial state
    if (type === 'setAuthString') { setAuthType(JSON.stringify(defaultAuth)); } else {
      setAuthType(defaultAuth);
    }
  }
};

export function AuthProvider({ children }: any): JSX.Element {
  const { Provider } = AuthContext;
  const [authString, setAuthString] = usePersistedState('auth', JSON.stringify(defaultAuth));
  useEffect(() => {
    (async () => {
      if (typeof authString === 'string') {
        try {
          const auth = JSON.parse(authString);
          const { token } = auth;
          const { sub } = jwt.decode(token, process.env.HashString as string);
          await setUserAuth(token, sub, setAuthString as (arg0: any) => void, 'setAuthString');
        } catch (err) { (setAuthString as React.Dispatch<React.SetStateAction<string>>)(JSON.stringify(defaultAuth)); }
      }
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  const { auth, setAuth } = configAuth(
    authString as string,
    setAuthString as React.Dispatch<React.SetStateAction<string>>,
  );
  return (<Provider value={{ auth, setAuth }}>{children}</Provider>
  );
}