1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

fix: show notification when app updates

This commit is contained in:
Ivar Conradi Østhus 2020-09-25 21:09:26 +02:00
parent 9acb0bb8b3
commit 31398571b4
7 changed files with 42 additions and 14 deletions

View File

@ -1,9 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly if no application 1`] = ` exports[`renders correctly if no application 1`] = `
<react-mdl-ProgressBar <div>
indeterminate={true} <p>
/> Loading...
</p>
<react-mdl-ProgressBar
indeterminate={true}
/>
</div>
`; `;
exports[`renders correctly with permissions 1`] = ` exports[`renders correctly with permissions 1`] = `

View File

@ -11,9 +11,11 @@ test('renders correctly if no application', () => {
const tree = renderer const tree = renderer
.create( .create(
<ClientApplications <ClientApplications
fetchApplication={jest.fn()} fetchApplication={() => Promise.resolve({})}
storeApplicationMetaData={jest.fn()} storeApplicationMetaData={jest.fn()}
deleteApplication={jest.fn()}
hasPermission={() => true} hasPermission={() => true}
history={{}}
/> />
) )
.toJSON(); .toJSON();
@ -26,8 +28,10 @@ test('renders correctly without permission', () => {
.create( .create(
<MemoryRouter> <MemoryRouter>
<ClientApplications <ClientApplications
fetchApplication={jest.fn()} fetchApplication={() => Promise.resolve({})}
storeApplicationMetaData={jest.fn()} storeApplicationMetaData={jest.fn()}
deleteApplication={jest.fn()}
history={{}}
application={{ application={{
appName: 'test-app', appName: 'test-app',
instances: [ instances: [
@ -80,8 +84,10 @@ test('renders correctly with permissions', () => {
.create( .create(
<MemoryRouter> <MemoryRouter>
<ClientApplications <ClientApplications
fetchApplication={jest.fn()} fetchApplication={() => Promise.resolve({})}
storeApplicationMetaData={jest.fn()} storeApplicationMetaData={jest.fn()}
history={{}}
deleteApplication={jest.fn()}
application={{ application={{
appName: 'test-app', appName: 'test-app',
instances: [ instances: [

View File

@ -21,13 +21,13 @@ class ClientApplications extends PureComponent {
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired,
}; };
constructor() { constructor(props) {
super(); super();
this.state = { activeTab: 0 }; this.state = { activeTab: 0, loading: !props.application };
} }
componentDidMount() { componentDidMount() {
this.props.fetchApplication(this.props.appName); this.props.fetchApplication(this.props.appName).finally(() => this.setState({ loading: false }));
} }
formatFullDateTime = v => formatFullDateTimeWithLocale(v, this.props.location.locale); formatFullDateTime = v => formatFullDateTimeWithLocale(v, this.props.location.locale);
@ -39,8 +39,15 @@ class ClientApplications extends PureComponent {
}; };
render() { render() {
if (!this.props.application) { if (this.state.loading) {
return <ProgressBar indeterminate />; return (
<div>
<p>Loading...</p>
<ProgressBar indeterminate />
</div>
);
} else if (!this.props.application) {
return <p>Application ({this.props.appName}) not found</p>;
} }
const { application, storeApplicationMetaData, hasPermission } = this.props; const { application, storeApplicationMetaData, hasPermission } = this.props;
const { appName, instances, strategies, seenToggles, url, description, icon = 'apps' } = application; const { appName, instances, strategies, seenToggles, url, description, icon = 'apps' } = application;

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { debounce } from 'debounce'; import { debounce } from 'debounce';
import { FABButton, Icon, Textfield } from 'react-mdl'; import { FABButton, Icon, Textfield } from 'react-mdl';
function SearchField({ value, updateValue }) { function SearchField({ value = '', updateValue }) {
const [localValue, setLocalValue] = useState(value); const [localValue, setLocalValue] = useState(value);
const debounceUpdateValue = debounce(updateValue, 500); const debounceUpdateValue = debounce(updateValue, 500);
@ -43,7 +43,7 @@ function SearchField({ value, updateValue }) {
} }
SearchField.propTypes = { SearchField.propTypes = {
value: PropTypes.string.isRequired, value: PropTypes.string,
updateValue: PropTypes.func.isRequired, updateValue: PropTypes.func.isRequired,
}; };

View File

@ -18,6 +18,7 @@ exports[`renders correctly with one feature 1`] = `
"width": "500px", "width": "500px",
} }
} }
value=""
/> />
<react-mdl-FABButton <react-mdl-FABButton
className="mdl-cell--hide-phone" className="mdl-cell--hide-phone"
@ -216,6 +217,7 @@ exports[`renders correctly with one feature without permissions 1`] = `
"width": "500px", "width": "500px",
} }
} }
value=""
/> />
<react-mdl-FABButton <react-mdl-FABButton
className="mdl-cell--hide-phone" className="mdl-cell--hide-phone"

View File

@ -1,5 +1,6 @@
import api from '../../data/applications-api'; import api from '../../data/applications-api';
import { dispatchAndThrow } from '../util'; import { dispatchAndThrow } from '../util';
import { MUTE_ERROR } from '../error-actions';
export const RECEIVE_ALL_APPLICATIONS = 'RECEIVE_ALL_APPLICATIONS'; export const RECEIVE_ALL_APPLICATIONS = 'RECEIVE_ALL_APPLICATIONS';
export const ERROR_RECEIVE_ALL_APPLICATIONS = 'ERROR_RECEIVE_ALL_APPLICATIONS'; export const ERROR_RECEIVE_ALL_APPLICATIONS = 'ERROR_RECEIVE_ALL_APPLICATIONS';
@ -32,7 +33,11 @@ export function storeApplicationMetaData(appName, key, value) {
return dispatch => return dispatch =>
api api
.storeApplicationMetaData(appName, key, value) .storeApplicationMetaData(appName, key, value)
.then(() => dispatch({ type: UPDATE_APPLICATION_FIELD, appName, key, value })) .then(() => {
const info = `${appName} successfully updated!`;
setTimeout(() => dispatch({ type: MUTE_ERROR, error: info }), 1000);
dispatch({ type: UPDATE_APPLICATION_FIELD, appName, key, value, info });
})
.catch(dispatchAndThrow(dispatch, ERROR_UPDATING_APPLICATION_DATA)); .catch(dispatchAndThrow(dispatch, ERROR_UPDATING_APPLICATION_DATA));
} }

View File

@ -13,6 +13,8 @@ import { ERROR_UPDATING_STRATEGY, ERROR_CREATING_STRATEGY, ERROR_RECEIVE_STRATEG
import { ERROR_ADD_CONTEXT_FIELD, ERROR_UPDATE_CONTEXT_FIELD } from './context/actions'; import { ERROR_ADD_CONTEXT_FIELD, ERROR_UPDATE_CONTEXT_FIELD } from './context/actions';
import { UPDATE_APPLICATION_FIELD } from './application/actions';
import { FORBIDDEN } from './util'; import { FORBIDDEN } from './util';
const debug = require('debug')('unleash:error-store'); const debug = require('debug')('unleash:error-store');
@ -49,6 +51,7 @@ const strategies = (state = getInitState(), action) => {
return state.update('list', list => list.remove(list.indexOf(action.error))); return state.update('list', list => list.remove(list.indexOf(action.error)));
case UPDATE_FEATURE_TOGGLE: case UPDATE_FEATURE_TOGGLE:
case UPDATE_FEATURE_TOGGLE_STRATEGIES: case UPDATE_FEATURE_TOGGLE_STRATEGIES:
case UPDATE_APPLICATION_FIELD:
return addErrorIfNotAlreadyInList(state, action.info); return addErrorIfNotAlreadyInList(state, action.info);
default: default:
return state; return state;