diff --git a/src/components/data-table/index.js b/src/components/data-table/index.js index 8ddf7766d..ff12fda38 100644 --- a/src/components/data-table/index.js +++ b/src/components/data-table/index.js @@ -107,18 +107,23 @@ function DataTable({ return rowContainer.map((row, i) => { prepareRow(row); - const tableProps = row.getRowProps(); + const tableProps = {...row.getRowProps()}; tableProps.className = `${ row.depth >= 1 ? 'expanded' : '' }`; + delete tableProps.key; return ( - - {row.cells.map((cell) => { + + {row.cells.map((cell, i) => { + const cellProps = {...cell.getCellProps()}; + const cellKey = cellProps.key; + delete cellProps.key; return ( {cell.render('Cell')} @@ -135,9 +140,19 @@ function DataTable({ {headerGroups.map((headerGroup) => ( - + { + if (propName !== 'key') { + props[propName] = headerGroup.getHeaderGroupProps()[propName]; + } + return props; + }, {})}> {headerGroup.headers.map((column) => ( - )} - + {sumColumns && rows.length > 1 && ( diff --git a/src/features/achievements/index.js b/src/features/achievements/index.js index 34b468143..4f1640005 100644 --- a/src/features/achievements/index.js +++ b/src/features/achievements/index.js @@ -4,7 +4,7 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import equal from 'fast-deep-equal'; import doFetchAchievements from './do-fetch-achievements.mjs'; -import { langCode } from '../../modules/lang-helpers.js'; +import { langCode, useLangCode } from '../../modules/lang-helpers.js'; const initialState = { data: [], @@ -40,17 +40,24 @@ const achievementsSlice = createSlice({ export const achievementsReducer = achievementsSlice.reducer; -let fetchedData = false; +let fetchedLang = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useAchievementsData() { const dispatch = useDispatch(); const { data, status, error } = useSelector((state) => state.achievements); + const lang = useLangCode(); useEffect(() => { - if (!fetchedData) { - fetchedData = true; + if (fetchedLang !== lang) { + fetchedLang = lang; dispatch(fetchAchievements()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { @@ -58,10 +65,9 @@ export default function useAchievementsData() { }, 600000); } return () => { - clearInterval(refreshInterval); - refreshInterval = false; + clearRefreshInterval(); }; - }, [dispatch]); + }, [dispatch, lang]); return { data, status, error }; }; diff --git a/src/features/barters/index.js b/src/features/barters/index.js index 2bec55558..285ec2881 100644 --- a/src/features/barters/index.js +++ b/src/features/barters/index.js @@ -122,7 +122,7 @@ export const selectAllBarters = createSelector([selectBarters, selectQuests, sel return { ...barter, requiredItems: barter.requiredItems.reduce((requirements, req) => { - let matchedItem = items.find(it => it.id === req.item.id); + let matchedItem = items?.find(it => it.id === req.item.id); if (matchedItem) { requirements.push({ ...req, @@ -132,7 +132,7 @@ export const selectAllBarters = createSelector([selectBarters, selectQuests, sel return requirements; }, []), rewardItems: barter.rewardItems.reduce((requirements, req) => { - const matchedItem = items.find(it => it.id === req.item.id); + const matchedItem = items?.find(it => it.id === req.item.id); if (matchedItem) { requirements.push({ ...req, @@ -149,6 +149,11 @@ export const selectAllBarters = createSelector([selectBarters, selectQuests, sel let fetchedData = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useBartersData() { const dispatch = useDispatch(); const { status, error } = useSelector((state) => state.barters); @@ -160,6 +165,7 @@ export default function useBartersData() { if (!fetchedData) { fetchedData = true; dispatch(fetchBarters()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { @@ -167,8 +173,7 @@ export default function useBartersData() { }, 600000); } return () => { - clearInterval(refreshInterval); - refreshInterval = false; + clearRefreshInterval(); }; }, [dispatch]); diff --git a/src/features/bosses/index.js b/src/features/bosses/index.js index 6bef322b6..5fec51bc4 100644 --- a/src/features/bosses/index.js +++ b/src/features/bosses/index.js @@ -4,7 +4,7 @@ import { createSlice, createAsyncThunk, createSelector } from '@reduxjs/toolkit' import equal from 'fast-deep-equal'; import doFetchBosses from './do-fetch-bosses.mjs'; -import { langCode } from '../../modules/lang-helpers.js'; +import { langCode, useLangCode } from '../../modules/lang-helpers.js'; import { placeholderBosses } from '../../modules/placeholder-data.js'; import rawBossData from '../../data/boss.json'; import useMapsData from '../maps/index.js'; @@ -89,27 +89,36 @@ export const selectAllBosses = createSelector([selectBosses, selectMaps], (bosse return bosses; }); -let fetchedData = false; +let fetchedLang = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useBossesData() { const dispatch = useDispatch(); const { status, error } = useSelector((state) => state.bosses); const data = useSelector(selectAllBosses); useMapsData(); + const lang = useLangCode(); useEffect(() => { - if (!fetchedData) { - fetchedData = true; + if (fetchedLang !== lang) { + fetchedLang = lang; dispatch(fetchBosses()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { dispatch(fetchBosses()); }, 600000); } - return () => clearInterval(refreshInterval); - }, [dispatch]); + return () => { + clearRefreshInterval(); + }; + }, [dispatch, lang]); return { data, status, error }; }; diff --git a/src/features/crafts/index.js b/src/features/crafts/index.js index 6de1f8117..bed2fb261 100644 --- a/src/features/crafts/index.js +++ b/src/features/crafts/index.js @@ -161,6 +161,11 @@ export const selectAllCrafts = createSelector([selectCrafts, selectQuests, selec let fetchedData = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useCraftsData() { const dispatch = useDispatch(); const { status, error } = useSelector((state) => state.crafts); @@ -172,6 +177,7 @@ export default function useCraftsData() { if (!fetchedData) { fetchedData = true; dispatch(fetchCrafts()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { @@ -179,8 +185,7 @@ export default function useCraftsData() { }, 600000); } return () => { - clearInterval(refreshInterval); - refreshInterval = false; + clearRefreshInterval(); }; }, [dispatch]); diff --git a/src/features/hideout/index.js b/src/features/hideout/index.js index 1b6fa7e1b..61679ab85 100644 --- a/src/features/hideout/index.js +++ b/src/features/hideout/index.js @@ -5,7 +5,7 @@ import equal from 'fast-deep-equal'; import doFetchHideout from './do-fetch-hideout.mjs'; -import { langCode } from '../../modules/lang-helpers.js'; +import { langCode, useLangCode } from '../../modules/lang-helpers.js'; import { placeholderHideout } from '../../modules/placeholder-data.js'; const initialState = { @@ -46,17 +46,24 @@ export const hideoutReducer = hideoutSlice.reducer; export const selectAllHideoutModules = (state) => state.hideout.data; -let fetchedData = false; +let fetchedLang = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useHideoutData() { const dispatch = useDispatch(); const { data, status, error } = useSelector((state) => state.hideout); + const lang = useLangCode(); useEffect(() => { - if (!fetchedData) { - fetchedData = true; + if (fetchedLang !== lang) { + fetchedLang = lang; dispatch(fetchHideout()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { @@ -64,10 +71,9 @@ export default function useHideoutData() { }, 600000); } return () => { - clearInterval(refreshInterval); - refreshInterval = false; + clearRefreshInterval(); }; - }, [dispatch]); + }, [dispatch, lang]); return { data, status, error }; }; diff --git a/src/features/items/index.js b/src/features/items/index.js index 53a246cff..ef1131bdf 100644 --- a/src/features/items/index.js +++ b/src/features/items/index.js @@ -5,7 +5,7 @@ import equal from 'fast-deep-equal'; import doFetchItems from './do-fetch-items.mjs'; import { placeholderItems } from '../../modules/placeholder-data.js'; -import { langCode } from '../../modules/lang-helpers.js'; +import { langCode, useLangCode } from '../../modules/lang-helpers.js'; const initialState = { data: placeholderItems(langCode()), @@ -52,17 +52,24 @@ export const itemsReducer = itemsSlice.reducer; export const selectAllItems = (state) => state.items.data; -let fetchedData = false; +let fetchedLang = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useItemsData() { const dispatch = useDispatch(); const { data, status, error } = useSelector((state) => state.items); + const lang = useLangCode(); useEffect(() => { - if (!fetchedData) { - fetchedData = true; + if (fetchedLang !== lang) { + fetchedLang = lang; dispatch(fetchItems()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { @@ -70,10 +77,9 @@ export default function useItemsData() { }, 600000); } return () => { - clearInterval(refreshInterval); - refreshInterval = false; + clearRefreshInterval(); }; - }, [dispatch]); + }, [dispatch, lang]); return { data, status, error }; }; diff --git a/src/features/maps/index.js b/src/features/maps/index.js index d4d4f2cc7..5c856377f 100644 --- a/src/features/maps/index.js +++ b/src/features/maps/index.js @@ -17,7 +17,7 @@ import { } from '@mdi/js'; import doFetchMaps from './do-fetch-maps.mjs'; -import { langCode } from '../../modules/lang-helpers.js'; +import { langCode, useLangCode } from '../../modules/lang-helpers.js'; import { placeholderMaps } from '../../modules/placeholder-data.js'; import i18n from '../../i18n.js'; @@ -59,17 +59,24 @@ export const mapsReducer = mapsSlice.reducer; export const selectMaps = (state) => state.maps.data; -let fetchedData = false; +let fetchedLang = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useMapsData() { const dispatch = useDispatch(); const { data, status, error } = useSelector((state) => state.maps); + const lang = useLangCode(); useEffect(() => { - if (!fetchedData) { - fetchedData = true; + if (fetchedLang !== lang) { + fetchedLang = lang; dispatch(fetchMaps()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { @@ -77,10 +84,9 @@ export default function useMapsData() { }, 600000); } return () => { - clearInterval(refreshInterval); - refreshInterval = false; + clearRefreshInterval(); }; - }, [dispatch]); + }, [dispatch, lang]); return { data, status, error }; }; diff --git a/src/features/meta/index.js b/src/features/meta/index.js index 7d6482284..0950e327e 100644 --- a/src/features/meta/index.js +++ b/src/features/meta/index.js @@ -4,7 +4,7 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import equal from 'fast-deep-equal'; import doFetchMeta from './do-fetch-meta.mjs'; -import { langCode } from '../../modules/lang-helpers.js'; +import { langCode, useLangCode } from '../../modules/lang-helpers.js'; import { placeholderMeta } from '../../modules/placeholder-data.js'; const initialState = { @@ -43,17 +43,24 @@ export const metaReducer = metaSlice.reducer; export const selectMeta = (state) => state.meta.data; -let fetchedData = false; +let fetchedLang = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useMetaData() { const dispatch = useDispatch(); const { data, status, error } = useSelector((state) => state.meta); + const lang = useLangCode(); useEffect(() => { - if (!fetchedData) { - fetchedData = true; + if (fetchedLang !== lang) { + fetchedLang = lang; dispatch(fetchMeta()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { @@ -61,10 +68,9 @@ export default function useMetaData() { }, 600000); } return () => { - clearInterval(refreshInterval); - refreshInterval = false; + clearRefreshInterval(); }; - }, [dispatch]); + }, [dispatch, lang]); return { data, status, error }; }; diff --git a/src/features/quests/index.js b/src/features/quests/index.js index e6d07a4b4..15dbae296 100644 --- a/src/features/quests/index.js +++ b/src/features/quests/index.js @@ -4,7 +4,7 @@ import { createSlice, createAsyncThunk, createSelector } from '@reduxjs/toolkit' import equal from 'fast-deep-equal'; import doFetchQuests from './do-fetch-quests.mjs'; -import { langCode } from '../../modules/lang-helpers.js'; +import { langCode, useLangCode } from '../../modules/lang-helpers.js'; import { placeholderTasks } from '../../modules/placeholder-data.js'; const initialState = { @@ -108,18 +108,25 @@ export const selectQuestsWithActive = createSelector([selectQuests, selectTrader }); }); -let fetchedData = false; +let fetchedLang = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useQuestsData() { const dispatch = useDispatch(); const { status, error } = useSelector((state) => state.quests); const data = useSelector(selectQuestsWithActive); + const lang = useLangCode(); useEffect(() => { - if (!fetchedData) { - fetchedData = true; + if (fetchedLang !== lang) { + fetchedLang = lang; dispatch(fetchQuests()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { @@ -127,10 +134,9 @@ export default function useQuestsData() { }, 600000); } return () => { - clearInterval(refreshInterval); - refreshInterval = false; + clearRefreshInterval(); }; - }, [dispatch]); + }, [dispatch, lang]); return { data, status, error }; }; diff --git a/src/features/traders/index.js b/src/features/traders/index.js index c66a2911b..8209c01e9 100644 --- a/src/features/traders/index.js +++ b/src/features/traders/index.js @@ -3,7 +3,7 @@ import { useSelector, useDispatch } from 'react-redux'; import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import equal from 'fast-deep-equal'; -import { langCode } from '../../modules/lang-helpers.js'; +import { langCode, useLangCode } from '../../modules/lang-helpers.js'; import doFetchTraders from './do-fetch-traders.mjs'; import { placeholderTraders } from '../../modules/placeholder-data.js'; @@ -44,17 +44,24 @@ export const tradersReducer = tradersSlice.reducer; export const selectAllTraders = (state) => state.traders.data; -let fetchedData = false; +let fetchedLang = false; let refreshInterval = false; +const clearRefreshInterval = () => { + clearInterval(refreshInterval); + refreshInterval = false; +}; + export default function useTradersData() { const dispatch = useDispatch(); const { data, status, error } = useSelector((state) => state.traders); + const lang = useLangCode(); useEffect(() => { - if (!fetchedData) { - fetchedData = true; + if (fetchedLang !== lang) { + fetchedLang = lang; dispatch(fetchTraders()); + clearRefreshInterval(); } if (!refreshInterval) { refreshInterval = setInterval(() => { @@ -62,10 +69,9 @@ export default function useTradersData() { }, 600000); } return () => { - clearInterval(refreshInterval); - refreshInterval = false; + clearRefreshInterval(); }; - }, [dispatch]); + }, [dispatch, lang,]); return { data, status, error }; }; diff --git a/src/modules/lang-helpers.js b/src/modules/lang-helpers.js index c35ec957e..3b2d4563c 100644 --- a/src/modules/lang-helpers.js +++ b/src/modules/lang-helpers.js @@ -1,7 +1,22 @@ // Helper function to convert an i18n language to a two digit language code +import { useEffect, useCallback, useState } from 'react'; + import i18n from '../i18n.js'; import languages from '../data/supported-languages.json'; +function validateLangCode(lng) { + // Convert to two character language code + const langFmt = lng.replace(/-[a-zA-Z]{2}/, ""); + + // Check if the language is supported + if (languages.includes(langFmt)) { + return langFmt; + } else { + // If the language is not supported, fall back to en + return 'en'; + } +} + export function langCode() { if (!i18n.isInitialized) { //console.log('i18n still not ready'); @@ -19,3 +34,22 @@ export function langCode() { return 'en'; } }; + +export const on = i18n.on; + +export const off = i18n.off; + +export function useLangCode() { + const [lang, setLang] = useState(langCode()); + + const eventListener = useCallback((lng) => { + setLang(validateLangCode(lng)); + }, [setLang]); + + useEffect(() => { + i18n.on('languageChanged', eventListener); + return () => i18n.off('languageChanged', eventListener); + }, [eventListener]); + + return lang; +}
+ { + if (propName !== 'key') { + props[propName] = column.getHeaderProps(column.getSortByToggleProps({ title: undefined }))[propName]; + } + return props; + }, {})}> {column.render('Header')} @@ -176,7 +191,7 @@ function DataTable({