farahat80/react-open-weather

View on GitHub
src/js/providers/visualcrossing/useVisualCrossing.js

Summary

Maintainability
B
5 hrs
Test Coverage
import { useEffect, useReducer, useState } from 'react';
import dayjs from 'dayjs';
import utc  from "dayjs/plugin/utc"
import timezone from "dayjs/plugin/timezone"
import axios from 'axios';
import { getIcon } from './iconsMap';

dayjs.extend(utc)
dayjs.extend(timezone)

export const formatDate = (dte, lang, tz) => {
  if (lang && lang !== 'en') {
    dayjs.locale(lang.replace('_', '-'));
  }
  if (dte && dayjs().isValid(dte)) {
    let date=dayjs.unix(dte);
    //without the timezone shift, the output time may be at the whim of the local JavaScript engine timezone 
    if (tz) date=date.tz(tz);
    return date.format('ddd D MMMM');
  }
  return '';
};

export const mapCurrent = (day, current, lang) => {
  return {
    date: formatDate(day.datetimeEpoch, lang),
    description: day ? day.description : null,
    icon: current && getIcon(current.icon),
    temperature: {
      current: current.temp.toFixed(0),
      min: day.tempmin.toFixed(0), 
      max: day.tempmax.toFixed(0),
    },
    wind: current.windspeed.toFixed(0),
    humidity: current.humidity,
  };
};

export const mapForecast = (days, lang) => {
  const mappedForecast = [];
  
  for (let i = 0; i < 5; i += 1) {
    mappedForecast.push({
      date: formatDate(days[i].datetimeEpoch, lang),
      description: days[i].description,
      icon: getIcon(days[i].icon),
      temperature: {
        min: days[i].tempmin.toFixed(0),
        max: days[i].tempmax.toFixed(0),
      },
      wind: days[i].windspeed.toFixed(0),
      humidity: days[i].humidity,
    });
  }
  return mappedForecast;
};

export const mapData = (weatherData, lang) => {
  const mapped = {};
  if (weatherData) {
    const tz=weatherData.timezone;
    const days = weatherData.days;
    const current =weatherData.currentConditions;
    const today=days && days[0]; //assuming forecast response
    mapped.current = mapCurrent(today, current, lang, tz);
    mapped.forecast = mapForecast(days, lang, tz);
  }
  return mapped;
};

export const SUCCESS = 'SUCCESS';
export const FAILURE = 'FAILURE';

const initialState = {
  data: null,
  errorMessage: null,
};

export const fetchReducer = (state, { type, payload }) => {
  switch (type) {
    case SUCCESS:
      return {
        data: payload,
        errorMessage: null,
      };
    case FAILURE:
      return { data: null, errorMessage: payload };
    default:
      return state;
  }
};

const useVisualCrossing = (options) => {
 
  
  const [state, dispatch] = useReducer(fetchReducer, initialState);
  const { data, errorMessage } = state;
  const [isLoading, setIsLoading] = useState(false);
  const { unit, lang, key, lon, lat } = options;
  //end point supports addresses too but stay with lat,lon
  const endpoint = `https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/${lat},${lon}`;
  const params = {
    key: key,
    lang,
    unitGroup: unit, //metric, us
    iconSet: "icons2", //use updated icons
    include:"days,current", //reduce response data to data we need
    elements:"datetimeEpoch,tempmax,tempmin,temp,humidity,windspeed,icon,description", //reduce response data size to data we need
  };

  const fetchData = async () => {
    setIsLoading(true);
    try {
      const weatherResponse = await axios.get(endpoint, { params });
      const payload = mapData(
        weatherResponse.data,
        lang,
      );

      dispatch({
        type: SUCCESS,
        payload,
      });
    } catch (error) {
      
      dispatch({ type: FAILURE, payload: error.message || error });
    }
    setIsLoading(false);
  };
  useEffect(() => {
    fetchData();
  }, [lon, lat]);
  return { data, isLoading, errorMessage, fetchData };
};

export default useVisualCrossing;