diff --git a/console/src/components/Services/Events/CronTriggers/Container.tsx b/console/src/components/Services/Events/CronTriggers/Container.tsx index 820622f140a..78939529822 100644 --- a/console/src/components/Services/Events/CronTriggers/Container.tsx +++ b/console/src/components/Services/Events/CronTriggers/Container.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import Helmet from 'react-helmet'; import CommonTabLayout from '../../../Common/Layout/CommonTabLayout/CommonTabLayout'; import { @@ -21,7 +21,12 @@ interface Props { allTriggers: Triggers; tabName: STTab; dispatch: Dispatch; + eventsLoading?: boolean; } +type TriggerPresence = + | 'not-missing' + | { type: 'timeout'; timeoutHandle: number } + | 'error-not-found'; const STContainer: React.FC = ({ triggerName, @@ -29,7 +34,11 @@ const STContainer: React.FC = ({ allTriggers, tabName, dispatch, + eventsLoading, }) => { + const [triggerPresence, setTriggerPresence] = useState( + 'not-missing' + ); React.useEffect(() => { dispatch(setCurrentTrigger(triggerName)); return () => { @@ -39,8 +48,28 @@ const STContainer: React.FC = ({ const currentTrigger = findScheduledTrigger(allTriggers, triggerName); + // TODO: This is a hack to deal with renaming cron triggers and stale props + // https://react-redux.js.org/api/hooks#stale-props-and-zombie-children + // Needs remodelling the state and careful handling of cron triggers rename + useEffect(() => { + if (currentTrigger) { + if ( + typeof triggerPresence === 'object' && + triggerPresence.type === 'timeout' + ) { + window.clearTimeout(triggerPresence.timeoutHandle); + setTriggerPresence('not-missing'); + } + } else if (triggerPresence === 'not-missing') { + const timeoutHandle = window.setTimeout(() => { + setTriggerPresence('error-not-found'); + }, 1200 /* arbitrary value */); + setTriggerPresence({ type: 'timeout', timeoutHandle }); + } + }, [currentTrigger]); + if (!currentTrigger) { - dispatch(setCurrentTrigger('')); + if (eventsLoading || triggerPresence !== 'error-not-found') return null; throw new NotFoundError(); } diff --git a/console/src/components/Services/Events/CronTriggers/Modify/index.tsx b/console/src/components/Services/Events/CronTriggers/Modify/index.tsx index 171df298081..a394e76af4f 100644 --- a/console/src/components/Services/Events/CronTriggers/Modify/index.tsx +++ b/console/src/components/Services/Events/CronTriggers/Modify/index.tsx @@ -1,21 +1,27 @@ import React from 'react'; import { connect, ConnectedProps } from 'react-redux'; import STContainer from '../Container'; -import { Triggers, RouterTriggerProps } from '../../types'; -import { MapStateToProps } from '../../../../../types'; import Modify from './Modify'; import { mapDispatchToPropsEmpty } from '../../../../Common/utils/reactUtils'; +import { ReduxState } from '../../../../../types'; interface Props extends InjectedProps {} const ModifyContainer: React.FC = props => { - const { dispatch, allTriggers, triggerName, readOnlyMode } = props; + const { + dispatch, + allTriggers, + triggerName, + readOnlyMode, + eventsLoading, + } = props; return ( {readOnlyMode ? ( 'Cannot modify in read-only mode' @@ -26,20 +32,15 @@ const ModifyContainer: React.FC = props => { ); }; -type PropsFromState = { - allTriggers: Triggers; - triggerName: string; - readOnlyMode: boolean; -}; - -const mapStateToProps: MapStateToProps = ( - state, - ownProps +const mapStateToProps = ( + state: ReduxState, + ownProps: { params: { triggerName: string } } ) => { return { allTriggers: state.events.triggers, readOnlyMode: state.main.readOnlyMode, triggerName: ownProps.params.triggerName, + eventsLoading: state.events.loading, }; }; diff --git a/console/src/components/Services/Events/ServerIO.ts b/console/src/components/Services/Events/ServerIO.ts index 29ee934e99e..3fccee3162e 100644 --- a/console/src/components/Services/Events/ServerIO.ts +++ b/console/src/components/Services/Events/ServerIO.ts @@ -1,4 +1,4 @@ -import { push } from 'react-router-redux'; +import { push, replace } from 'react-router-redux'; import { fetchEventTriggersQuery, fetchScheduledTriggersQuery, @@ -35,8 +35,14 @@ import { EventTrigger, EventKind, InvocationLog, + LOADING_TRIGGERS, } from './types'; -import { setScheduledTriggers, setEventTriggers, setTriggers } from './reducer'; +import { + setScheduledTriggers, + setEventTriggers, + setTriggers, + setCurrentTrigger, +} from './reducer'; import { LocalScheduledTriggerState } from './CronTriggers/state'; import { LocalAdhocEventState } from './AdhocEvents/Add/state'; import { @@ -59,6 +65,8 @@ import { getLogsTableDef } from './utils'; export const fetchTriggers = ( kind: Nullable ): Thunk> => (dispatch, getState) => { + dispatch({ type: LOADING_TRIGGERS }); + const bulkQueryArgs = []; if (kind) { bulkQueryArgs.push( @@ -194,8 +202,8 @@ export const saveScheduledTrigger = ( ); const upRenameQueries = [ - getDropScheduledTriggerQuery(existingTrigger.name), generateCreateScheduledTriggerQuery(state), + getDropScheduledTriggerQuery(existingTrigger.name), ]; const downRenameQueries = [ getDropScheduledTriggerQuery(state.name), @@ -209,18 +217,19 @@ export const saveScheduledTrigger = ( const successMsg = 'Updated scheduled trigger successfully'; const customOnSuccess = () => { - if (isRenamed) { - const newHref = window.location.href.replace( - getSTModifyRoute(existingTrigger.name, 'relative'), - getSTModifyRoute(state.name, 'relative') - ); - return window.location.replace(newHref); - } return dispatch(fetchTriggers('cron')) .then(() => { if (successCb) { successCb(); } + if (isRenamed) { + const newHref = window.location.href.replace( + getSTModifyRoute(existingTrigger.name, 'relative'), + getSTModifyRoute(state.name, 'relative') + ); + dispatch(replace(newHref)); + dispatch(setCurrentTrigger(state.name)); + } }) .catch(() => { if (errorCb) { diff --git a/console/src/components/Services/Events/reducer.ts b/console/src/components/Services/Events/reducer.ts index 855defdad6b..74aa601dbb1 100644 --- a/console/src/components/Services/Events/reducer.ts +++ b/console/src/components/Services/Events/reducer.ts @@ -12,6 +12,7 @@ import { RASetCurrentTrigger, RASetEventTriggers, RASetScheduledTriggers, + LOADING_TRIGGERS, } from './types'; export const setTriggers = (data: Triggers): RASetAllTriggers => ({ @@ -41,6 +42,7 @@ const reducer = (state = defaultState, action: RAEvents) => { return { ...state, triggers: action.data, + loading: false, }; case LOADED_SCHEDULED_TRIGGERS: return { @@ -49,6 +51,7 @@ const reducer = (state = defaultState, action: RAEvents) => { ...state.triggers, scheduled: action.data, }, + loading: false, }; case LOADED_EVENT_TRIGGERS: return { @@ -57,12 +60,18 @@ const reducer = (state = defaultState, action: RAEvents) => { ...state.triggers, event: action.data, }, + loading: false, }; case SET_CURRENT_TRIGGER: return { ...state, currentTrigger: action.name, }; + case LOADING_TRIGGERS: + return { + ...state, + loading: true, + }; default: return state; } diff --git a/console/src/components/Services/Events/state.ts b/console/src/components/Services/Events/state.ts index 3cf24b2097d..72a69adbd82 100644 --- a/console/src/components/Services/Events/state.ts +++ b/console/src/components/Services/Events/state.ts @@ -3,9 +3,7 @@ import { Triggers } from './types'; export type EventsState = { triggers: Triggers; currentTrigger: string; - loading: { - [component: string]: boolean; - }; + loading: boolean; error: { [component: string]: any; }; @@ -17,7 +15,7 @@ const state: EventsState = { event: [], }, currentTrigger: '', - loading: {}, + loading: true, error: {}, }; diff --git a/console/src/components/Services/Events/types.ts b/console/src/components/Services/Events/types.ts index acc423d8eb3..151b598d7b8 100644 --- a/console/src/components/Services/Events/types.ts +++ b/console/src/components/Services/Events/types.ts @@ -6,11 +6,8 @@ import { Dispatch } from '../../../types'; export const LOADING_TRIGGERS = 'Events/LOADING_TRIGGERS'; export const LOADED_TRIGGERS = 'Events/LOADED_TRIGGERS'; -export const LOADING_SCHEDULED_TRIGGERS = 'Events/LOADING_SCHEDULED_TRIGGERS'; export const LOADED_SCHEDULED_TRIGGERS = 'Events/LOADED_SCHEDULED_TRIGGERS'; -export const LOADING_EVENT_TRIGGERS = 'Events/LOADING_EVENT_TRIGGERS'; export const LOADED_EVENT_TRIGGERS = 'Events/LOADED_EVENT_TRIGGERS'; -export const LOADING_TRIGGERS_ERROR = 'Events/LOADING_TRIGGERS_ERROR'; export const SET_CURRENT_TRIGGER = 'Events/SET_CURRENT_TRIGGER'; export const LOAD_PENDING_DATA_EVENTS = 'Events/LOAD_PENDING_DATA_EVENTS'; @@ -161,4 +158,5 @@ export type RAEvents = | RASetAllTriggers | RASetScheduledTriggers | RASetEventTriggers - | RASetCurrentTrigger; + | RASetCurrentTrigger + | { type: typeof LOADING_TRIGGERS };