From 50deb7aaaaecc0116c1127da223885b4d1bef231 Mon Sep 17 00:00:00 2001 From: E Kelen Date: Tue, 16 Jun 2020 16:18:33 +0200 Subject: [PATCH] refactor(front): rename Result.. -> Global.., split out autorefresh hook (#349) rename ResultStore -> GlobalStore remove current autorefresh add recursive timout hook remove stuff from recursivetimeout rename ResultContext -> GlobalContext Signed-off-by: Elizabeth Kelen --- web/src/App.js | 6 +-- web/src/hooks/useRecursiveTimeout.js | 33 +++++++++++++ .../store/{ResultStore.js => GlobalStore.js} | 12 +++-- web/src/ui/components/Build/BuildContainer.js | 4 +- .../ui/components/FilterModal/FilterModal.js | 4 +- web/src/ui/components/Filters/Filters.js | 10 ++-- web/src/ui/components/Header/Header.js | 4 +- web/src/ui/pages/Home/Home.js | 48 +++++++------------ 8 files changed, 73 insertions(+), 48 deletions(-) create mode 100644 web/src/hooks/useRecursiveTimeout.js rename web/src/store/{ResultStore.js => GlobalStore.js} (81%) diff --git a/web/src/App.js b/web/src/App.js index 69393bec..0aee18d2 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -6,7 +6,7 @@ import { } from 'react-router-dom' import 'tabler-react/dist/Tabler.css' import './assets/main.scss' -import { ResultStore } from './store/ResultStore' +import { GlobalStore } from './store/GlobalStore' import { ThemeContext, ThemeStore } from './store/ThemeStore' import Error404 from './ui/pages/Error404/Error404' import Home from './ui/pages/Home/Home' @@ -36,9 +36,9 @@ const AppRouter = () => { const App = () => ( - + - + ) diff --git a/web/src/hooks/useRecursiveTimeout.js b/web/src/hooks/useRecursiveTimeout.js new file mode 100644 index 00000000..9979cf9d --- /dev/null +++ b/web/src/hooks/useRecursiveTimeout.js @@ -0,0 +1,33 @@ +import { useEffect, useRef } from 'react' + +/** + * Source: https://www.aaron-powell.com/posts/2019-09-23-recursive-settimeout-with-react-hooks/ + */ +export const useRecursiveTimeout = ( + callback = () => { }, + delay = 0, +) => { + const savedCallback = useRef(callback) + + // Remember the latest callback. + useEffect(() => { + savedCallback.current = callback + }, [callback]) + + // Set up the timeout loop. + // eslint-disable-next-line consistent-return + useEffect(() => { + let timerId + function tick() { + savedCallback.current() + + if (delay !== null) { + timerId = setTimeout(tick, delay) + } + } + if (delay !== null) { + timerId = setTimeout(tick, delay) + return () => timerId && clearTimeout(timerId) + } + }, [delay]) +} diff --git a/web/src/store/ResultStore.js b/web/src/store/GlobalStore.js similarity index 81% rename from web/src/store/ResultStore.js rename to web/src/store/GlobalStore.js index 49ee0d6d..efbe04b4 100644 --- a/web/src/store/ResultStore.js +++ b/web/src/store/GlobalStore.js @@ -18,7 +18,7 @@ import { PROJECT_BUILD_DRIVER, } from '../constants' -export const ResultContext = React.createContext() +export const GlobalContext = React.createContext() export const INITIAL_STATE = { apiKey: retrieveAuthCookie() || null, @@ -51,22 +51,26 @@ function reducer(state, action) { } } -export const ResultStore = ({ children }) => { +export const GlobalStore = ({ children }) => { const [state, dispatch] = useReducer(reducer, INITIAL_STATE) // custom actions can go here; for now we just have one + // 🚧 not cool, we need to split this so we don't get redundant re-renders + // or have to do a slow deep equality check to prevent it const updateState = (payload) => { dispatch({ type: actions.UPDATE_STATE, payload: cloneDeep(payload) }) } + return ( - {children} - + ) } diff --git a/web/src/ui/components/Build/BuildContainer.js b/web/src/ui/components/Build/BuildContainer.js index 6265c034..4a437906 100644 --- a/web/src/ui/components/Build/BuildContainer.js +++ b/web/src/ui/components/Build/BuildContainer.js @@ -2,7 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { get } from 'lodash' import React, { useContext, useState } from 'react' import { ARTIFACT_KIND_NAMES, BRANCH, BUILD_STATE } from '../../../constants' -import { ResultContext } from '../../../store/ResultStore' +import { GlobalContext } from '../../../store/GlobalStore' import { ThemeContext } from '../../../store/ThemeStore' import { getIsArray, getIsArrayWithN, getStrEquNormalized } from '../../../util/getters' import { getArtifactKindIcon } from '../../styleTools/brandIcons' @@ -51,7 +51,7 @@ const AnyRunningBuildTags = ({ hasRunningBuilds, allBuildsForMr, theme }) => (ha const BuildContainer = React.memo(({ build, toCollapse, children, hasRunningBuilds, }) => { - const { state } = useContext(ResultContext) + const { state } = useContext(GlobalContext) const [collapsed, toggleCollapsed] = useState(toCollapse) const [showingAllBuilds, toggleShowingAllBuilds] = useState(false) const { theme } = useContext(ThemeContext) diff --git a/web/src/ui/components/FilterModal/FilterModal.js b/web/src/ui/components/FilterModal/FilterModal.js index 51c4d71e..37726725 100644 --- a/web/src/ui/components/FilterModal/FilterModal.js +++ b/web/src/ui/components/FilterModal/FilterModal.js @@ -4,7 +4,7 @@ import { removeAuthCookie } from '../../../api/cookies' import { ARTIFACT_KINDS, BUILD_DRIVERS, BUILD_STATES, PROJECT, PROJECTS, } from '../../../constants' -import { INITIAL_STATE, ResultContext } from '../../../store/ResultStore' +import { INITIAL_STATE, GlobalContext } from '../../../store/GlobalStore' import { ThemeContext } from '../../../store/ThemeStore' import Tag from '../Tag/Tag' import ThemeToggler from '../ThemeToggler' @@ -68,7 +68,7 @@ const BuildStateWidgets = ({ selectedBuildStates, setSelectedBuildStates }) => ( const FilterModal = ({ closeAction }) => { const { theme, themeStyles } = useContext(ThemeContext) - const { state, updateState } = useContext(ResultContext) + const { state, updateState } = useContext(GlobalContext) const [selectedDrivers, setSelectedDrivers] = useState([ ...state.uiFilters.build_driver, ]) diff --git a/web/src/ui/components/Filters/Filters.js b/web/src/ui/components/Filters/Filters.js index 93d28311..7eec52a1 100644 --- a/web/src/ui/components/Filters/Filters.js +++ b/web/src/ui/components/Filters/Filters.js @@ -8,14 +8,14 @@ import { removeAuthCookie } from '../../../api/cookies' import { ARTIFACT_VALUE_KIND, BUILD_DRIVERS, BUILD_DRIVER_TO_NAME, BUILD_STATES, BUILD_STATE_VALUE_TO_NAME, PROJECT, PROJECT_NAME, } from '../../../constants' -import { ResultContext } from '../../../store/ResultStore.js' +import { GlobalContext } from '../../../store/GlobalStore.js' import { getIsArrayWithN } from '../../../util/getters.js' import { getArtifactKindIcon } from '../../styleTools/brandIcons.js' import OutlineWidget from '../OutlineWidget/OutlineWidget.js' import styles from './Filters.module.scss' import IconProjectBertyMessenger from '../../../assets/svg/IconProjectBertyMessenger' -const Filters = React.memo(({ autoRefreshOn, onFilterClick, setAutoRefreshOn }) => { +const Filters = React.memo(({ autoRefreshOn = false, onFilterClick = () => { }, setAutoRefreshOn = () => { } }) => { const { state: { uiFilters: { @@ -25,7 +25,7 @@ const Filters = React.memo(({ autoRefreshOn, onFilterClick, setAutoRefreshOn }) projects, }, }, updateState, - } = useContext(ResultContext) + } = useContext(GlobalContext) const FilterMessenger = () => (projects.includes(PROJECT.messenger) && ( { }, }) => { - const { state, updateState } = useContext(ResultContext) + const { state, updateState } = useContext(GlobalContext) const history = useHistory() const location = useLocation() diff --git a/web/src/ui/pages/Home/Home.js b/web/src/ui/pages/Home/Home.js index d06b31ce..ac365b54 100644 --- a/web/src/ui/pages/Home/Home.js +++ b/web/src/ui/pages/Home/Home.js @@ -24,7 +24,7 @@ import { validateError } from '../../../api/apiResponseTransforms' import { ARTIFACT_KINDS, BUILD_DRIVERS, BUILD_STATES, DEFAULT_RESULT_REQUEST_LIMIT, PLATFORM_TO_ARTIFACT_KIND, KIND_TO_PLATFORM, } from '../../../constants' -import { INITIAL_STATE, ResultContext } from '../../../store/ResultStore' +import { INITIAL_STATE, GlobalContext } from '../../../store/GlobalStore' import { ThemeContext } from '../../../store/ThemeStore' import { getMobileOperatingSystem } from '../../../util/browser' import { singleItemToArray } from '../../../util/getters' @@ -36,10 +36,11 @@ import Header from '../../components/Header/Header' import ProtocolDisclaimer from '../../components/ProtocolDisclaimer' import ShowFiltersButton from '../../components/ShowFiltersButton' import styles from './Home.module.scss' +import { useRecursiveTimeout } from '../../../hooks/useRecursiveTimeout' const Home = () => { const { theme } = useContext(ThemeContext) - const { state, updateState } = useContext(ResultContext) + const { state, updateState } = useContext(GlobalContext) const [showingFiltersModal, toggleShowFilters] = useState(false) const [showingDisclaimerModal, toggleShowDisclaimer] = useState(false) const [needsNewFetch, setNeedsNewFetch] = useState(false) @@ -47,6 +48,14 @@ const Home = () => { const { search: locationSearch } = useLocation() const history = useHistory() + useRecursiveTimeout(() => { + if (autoRefreshOn && !showingFiltersModal && !showingDisclaimerModal) { + updateState({ + needsRefresh: true, + }) + } + }, 10 * 1000) + // 🚧 Hacky - On initial render, set/get query params based on URL bar or state.uiFilters useEffect(() => { if (!locationSearch) { @@ -183,33 +192,6 @@ const Home = () => { if (state.needsRefresh === true) triggerNewQuery() }, [state.needsRefresh]) - // 🚧 If autoRefresh is enabled, trigger API call every 10 sec - useEffect(() => { - let timer - if ( - !showingDisclaimerModal - && !showingFiltersModal - && autoRefreshOn - ) { - clearInterval(timer) - timer = setInterval(() => { - updateState({ - needsRefresh: true, - }) - }, 10000) - } else { - clearInterval(timer) - } - return () => { - clearInterval(timer) - } - }, [ - showingDisclaimerModal, - showingFiltersModal, - autoRefreshOn, - updateState, - ]) - // Show protocol warning modal + agreement on component render useEffect(() => { const disclaimerAccepted = Cookies.get('disclaimerAccepted') @@ -222,10 +204,15 @@ const Home = () => { toggleShowDisclaimer(!accepted) } + return (
-
+
{state.error && } {state.error && state.error.status === 401 && ( @@ -235,7 +222,6 @@ const Home = () => { )}