From d9ae9a1c51889d0c135028fcb8919dd20a4aa6a5 Mon Sep 17 00:00:00 2001 From: Justin Reynolds Date: Thu, 13 Jul 2017 17:06:15 -0700 Subject: [PATCH] fix(core): Fix application refresher (#3916) --- .../src/application/ApplicationComponent.tsx | 35 +++++---- .../executionGroup/execution/Execution.tsx | 3 +- .../src/presentation/refresher/Refresher.tsx | 74 ++++++++++++++----- .../src/presentation/refresher/refresher.less | 1 + 4 files changed, 79 insertions(+), 34 deletions(-) diff --git a/app/scripts/modules/core/src/application/ApplicationComponent.tsx b/app/scripts/modules/core/src/application/ApplicationComponent.tsx index 27f65352c92..3ae7ebc342c 100644 --- a/app/scripts/modules/core/src/application/ApplicationComponent.tsx +++ b/app/scripts/modules/core/src/application/ApplicationComponent.tsx @@ -6,7 +6,6 @@ import { NgReact, ReactInjector } from 'core/reactShims'; import { Refresher } from 'core/presentation/refresher/Refresher'; import { Tooltip } from 'core/presentation/Tooltip'; import { UIView } from '@uirouter/react'; -import { relativeTime, timestamp } from 'core/utils/timeFormatters'; import { DebugWindow } from 'core/utils/consoleDebug'; import './application.less'; @@ -17,10 +16,14 @@ export interface IApplicationComponentProps { export interface IApplicationComponentState { compactHeader: boolean; + refreshing: boolean; + lastRefresh: number; } @autoBindMethods export class ApplicationComponent extends React.Component { + private appRefreshUnsubscribe: () => void; + constructor(props: IApplicationComponentProps) { super(props); if (props.app.notFound) { @@ -28,11 +31,22 @@ export class ApplicationComponent extends React.Component 20 - }; + this.state = this.parseState(props); + DebugWindow.application = props.app; props.app.enableAutoRefresh(); + this.appRefreshUnsubscribe = props.app.onRefresh(null, () => { + this.setState(this.parseState(props)); + }); + } + + private parseState(props: IApplicationComponentProps): IApplicationComponentState { + const refreshState = props.app.activeState || props.app; + return { + compactHeader: props.app.name.length > 20, + lastRefresh: refreshState.lastRefresh, + refreshing: refreshState.refreshing + }; } public componentWillUnmount(): void { @@ -58,15 +72,6 @@ export class ApplicationComponent extends React.Component - {refresherState.refreshing &&

Application is refreshing.

} - {!refresherState.refreshing &&

(click to refresh)

} -

Last refresh: {timestamp(refresherState.lastRefresh)}
({relativeTime(refresherState.lastRefresh)})

-

Note: Due to caching, data may be delayed up to 2 minutes

- - ); const NotFound = this.props.app.notFound ? (
@@ -80,8 +85,8 @@ export class ApplicationComponent extends React.Component {this.props.app.name} diff --git a/app/scripts/modules/core/src/delivery/executionGroup/execution/Execution.tsx b/app/scripts/modules/core/src/delivery/executionGroup/execution/Execution.tsx index 4d930030195..f332fdea26f 100644 --- a/app/scripts/modules/core/src/delivery/executionGroup/execution/Execution.tsx +++ b/app/scripts/modules/core/src/delivery/executionGroup/execution/Execution.tsx @@ -10,6 +10,7 @@ import { Application } from 'core/application/application.model'; import { IExecution , IRestartDetails } from 'core/domain'; import { IExecutionViewState } from 'core/pipeline/config/graph/pipelineGraph.service'; import { IPipelineNode } from 'core/pipeline/config/graph/pipelineGraph.service'; +import { IScheduler } from 'core/scheduler/scheduler.factory'; import { OrchestratedItemRunningTime } from './OrchestratedItemRunningTime'; import { SETTINGS } from 'core/config/settings'; import { NgReact, ReactInjector } from 'core/reactShims'; @@ -45,7 +46,7 @@ export class Execution extends React.Component dataSourceKey: 'executions' }; - private activeRefresher: any; + private activeRefresher: IScheduler; private stateChangeSuccessSubscription: Subscription; private runningTime: OrchestratedItemRunningTime; diff --git a/app/scripts/modules/core/src/presentation/refresher/Refresher.tsx b/app/scripts/modules/core/src/presentation/refresher/Refresher.tsx index 018e929efbd..e13c9da95c8 100644 --- a/app/scripts/modules/core/src/presentation/refresher/Refresher.tsx +++ b/app/scripts/modules/core/src/presentation/refresher/Refresher.tsx @@ -2,39 +2,77 @@ import * as React from 'react'; import autoBindMethods from 'class-autobind-decorator'; import { $window } from 'ngimport'; +import { IScheduler } from 'core/scheduler/scheduler.factory'; +import { ReactInjector } from 'core/reactShims'; import { Tooltip } from 'core/presentation'; +import { relativeTime, timestamp } from 'core/utils/timeFormatters'; import './refresher.less'; export interface IRefresherProps { - state: { - refreshing: boolean; - lastRefresh: number; - }; - tooltipTemplate: JSX.Element; + refreshing: boolean; + lastRefresh: number; refresh: (e: React.MouseEvent) => void; } +export interface IRefresherState { + color: string; + relativeTime: string; +} + @autoBindMethods -export class Refresher extends React.Component { - public getAgeColor(): string { - const yellowAge = 2 * 60 * 1000; // 2 minutes - const redAge = 5 * 60 * 1000; // 5 minutes - const lastRefresh = this.props.state.lastRefresh || 0; +export class Refresher extends React.Component { + private activeRefresher: IScheduler; + private yellowAge = 2 * 60 * 1000; // 2 minutes + private redAge = 5 * 60 * 1000; // 5 minutes + + constructor(props: IRefresherProps) { + super(props); + + this.state = this.parseState(props); + + // Update the state on an interval to make sure the color and tooltip get updated + const { schedulerFactory } = ReactInjector; + this.activeRefresher = schedulerFactory.createScheduler(2000); + this.activeRefresher.subscribe(() => { + this.setState(this.parseState(this.props)); + }); + } + + public componentWillUnmount(): void { + if (this.activeRefresher) { + this.activeRefresher.unsubscribe(); + } + } + + public parseState(props: IRefresherProps): IRefresherState { + const lastRefresh = props.lastRefresh || 0; const age = new Date().getTime() - lastRefresh; - return age < yellowAge ? 'young' : - age < redAge ? 'old' : 'ancient'; + const color = age < this.yellowAge ? 'young' : + age < this.redAge ? 'old' : 'ancient'; + + return { + color, + relativeTime: relativeTime(this.props.lastRefresh) + }; } public render() { + const RefresherTooltip = ( + + {this.props.refreshing &&

Application is refreshing.

} + {!this.props.refreshing &&

(click to refresh)

} +

Last refresh: {timestamp(this.props.lastRefresh)}
({this.state.relativeTime})

+

Note: Due to caching, data may be delayed up to 2 minutes

+
+ ); + return ( - -
- - - -
+ + + + ); } diff --git a/app/scripts/modules/core/src/presentation/refresher/refresher.less b/app/scripts/modules/core/src/presentation/refresher/refresher.less index 1fe7d5daf28..3353e9ff3ee 100644 --- a/app/scripts/modules/core/src/presentation/refresher/refresher.less +++ b/app/scripts/modules/core/src/presentation/refresher/refresher.less @@ -1,6 +1,7 @@ @import (reference) "~core/presentation/less/imports/commonImports.less"; .refresher { + margin-top: 4px; .fa-refresh { font-size: 75%; &.refresh-disabled {