Skip to content

Commit

Permalink
fix(core): Fix application refresher (#3916)
Browse files Browse the repository at this point in the history
  • Loading branch information
Justin Reynolds authored Jul 14, 2017
1 parent 3b2e249 commit d9ae9a1
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 34 deletions.
35 changes: 20 additions & 15 deletions app/scripts/modules/core/src/application/ApplicationComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -17,22 +16,37 @@ export interface IApplicationComponentProps {

export interface IApplicationComponentState {
compactHeader: boolean;
refreshing: boolean;
lastRefresh: number;
}

@autoBindMethods
export class ApplicationComponent extends React.Component<IApplicationComponentProps, IApplicationComponentState> {
private appRefreshUnsubscribe: () => void;

constructor(props: IApplicationComponentProps) {
super(props);
if (props.app.notFound) {
ReactInjector.recentHistoryService.removeLastItem('applications');
return;
}

this.state = {
compactHeader: props.app.name.length > 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 {
Expand All @@ -58,15 +72,6 @@ export class ApplicationComponent extends React.Component<IApplicationComponentP

public render() {
const { ApplicationNav, ApplicationNavSecondary } = NgReact;
const refresherState = this.props.app.activeState || this.props.app;
const RefresherTooltip = (
<span>
{refresherState.refreshing && <p>Application is <strong>refreshing</strong>.</p>}
{!refresherState.refreshing && <p>(click <span className="fa fa-refresh"/> to refresh)</p>}
<p>Last refresh: {timestamp(refresherState.lastRefresh)} <br/> ({relativeTime(refresherState.lastRefresh)})</p>
<p className="small"><strong>Note:</strong> Due to caching, data may be delayed up to 2 minutes</p>
</span>
);

const NotFound = this.props.app.notFound ? (
<div>
Expand All @@ -80,8 +85,8 @@ export class ApplicationComponent extends React.Component<IApplicationComponentP
<i className="fa fa-window-maximize"/>
<span className="application-name">{this.props.app.name}</span>
<Refresher
state={refresherState}
tooltipTemplate={RefresherTooltip}
refreshing={this.state.refreshing}
lastRefresh={this.state.lastRefresh}
refresh={this.handleRefresh}
/>
</h2>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -45,7 +46,7 @@ export class Execution extends React.Component<IExecutionProps, IExecutionState>
dataSourceKey: 'executions'
};

private activeRefresher: any;
private activeRefresher: IScheduler;
private stateChangeSuccessSubscription: Subscription;
private runningTime: OrchestratedItemRunningTime;

Expand Down
74 changes: 56 additions & 18 deletions app/scripts/modules/core/src/presentation/refresher/Refresher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLElement>) => void;
}

export interface IRefresherState {
color: string;
relativeTime: string;
}

@autoBindMethods
export class Refresher extends React.Component<IRefresherProps, {}> {
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<IRefresherProps, IRefresherState> {
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 = (
<span>
{this.props.refreshing && <p>Application is <strong>refreshing</strong>.</p>}
{!this.props.refreshing && <p>(click <span className="fa fa-refresh"/> to refresh)</p>}
<p>Last refresh: {timestamp(this.props.lastRefresh)} <br/> ({this.state.relativeTime})</p>
<p className="small"><strong>Note:</strong> Due to caching, data may be delayed up to 2 minutes</p>
</span>
);

return (
<Tooltip template={this.props.tooltipTemplate} placement={$window.innerWidth < 1100 ? 'right' : 'bottom'}>
<div>
<a className="refresher clickable" onClick={this.props.refresh}>
<span className={`fa fa-refresh refresh-${this.getAgeColor()} ${this.props.state.refreshing ? 'fa-spin' : ''}`}/>
</a>
</div>
<Tooltip template={RefresherTooltip} placement={$window.innerWidth < 1100 ? 'right' : 'bottom'}>
<a className="refresher clickable" onClick={this.props.refresh}>
<span className={`fa fa-refresh refresh-${this.state.color} ${this.props.refreshing ? 'fa-spin' : ''}`}/>
</a>
</Tooltip>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@import (reference) "~core/presentation/less/imports/commonImports.less";

.refresher {
margin-top: 4px;
.fa-refresh {
font-size: 75%;
&.refresh-disabled {
Expand Down

0 comments on commit d9ae9a1

Please sign in to comment.