From 1a4f8e71d448b1239aef990fe5823023ab75e547 Mon Sep 17 00:00:00 2001 From: "Vladimir Pal (BeDoIt)" Date: Mon, 12 Feb 2018 19:39:06 +0200 Subject: [PATCH] chore: add ErrorBoundary component --- .../ErrorBoundary/ErrorBoundary.jsx | 34 +++++++++ app/components/ErrorBoundary/index.jsx | 4 + app/components/index.jsx | 2 + app/containers/App.jsx | 35 +++++---- app/containers/IssueView/IssueView.jsx | 75 +++++++++++-------- .../IssuesSourcePicker/IssuesSourcePicker.jsx | 3 + .../SidebarIssues/IssueItem/IssueItem.jsx | 56 +++++++------- .../Sidebar/SidebarIssues/SidebarIssues.jsx | 35 +++++---- app/package.json | 2 +- app/sagas/issues.js | 21 +++--- app/sagas/storage.js | 2 +- package.json | 2 +- 12 files changed, 173 insertions(+), 98 deletions(-) create mode 100644 app/components/ErrorBoundary/ErrorBoundary.jsx create mode 100644 app/components/ErrorBoundary/index.jsx diff --git a/app/components/ErrorBoundary/ErrorBoundary.jsx b/app/components/ErrorBoundary/ErrorBoundary.jsx new file mode 100644 index 000000000..e0c80fbdf --- /dev/null +++ b/app/components/ErrorBoundary/ErrorBoundary.jsx @@ -0,0 +1,34 @@ +// @flow +import React, { Component } from 'react'; +import Raven from 'raven-js'; + + +class ErrorBoundary extends Component { + state = { + hasError: false, + } + + componentDidCatch(error: any, info: any) { + this.setState({ + hasError: true, + }); + Raven.captureException( + error, + { + extra: { + info, + debugData: this.props.debugData, + }, + }, + ); + } + + render() { + if (this.state.hasError) { + return

Something went wrong.

; + } + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/app/components/ErrorBoundary/index.jsx b/app/components/ErrorBoundary/index.jsx new file mode 100644 index 000000000..46cebc025 --- /dev/null +++ b/app/components/ErrorBoundary/index.jsx @@ -0,0 +1,4 @@ +// @flow +import ErrorBoundary from './ErrorBoundary'; + +export default ErrorBoundary; diff --git a/app/components/index.jsx b/app/components/index.jsx index 7c9e379ca..d1643275b 100644 --- a/app/components/index.jsx +++ b/app/components/index.jsx @@ -1,3 +1,4 @@ +// @flow export Flex from './Flex'; export { AutosizableList, @@ -9,6 +10,7 @@ export { IssueViewPlaceholder, RecentItemsPlaceholder, } from './Placeholders'; +export ErrorBoundary from './ErrorBoundary'; export Img from './Img'; export Calendar from './Calendar'; export TextField from './TextField'; diff --git a/app/containers/App.jsx b/app/containers/App.jsx index 7844e2a04..3c197048f 100644 --- a/app/containers/App.jsx +++ b/app/containers/App.jsx @@ -21,6 +21,9 @@ import { AppWrapper, FullPageSpinner, } from 'styles'; +import { + ErrorBoundary, +} from 'components'; import AuthForm from './AuthForm'; import Main from './Main'; @@ -31,19 +34,25 @@ type Props = { }; const App: StatelessFunctionalComponent = (props: Props): Node => ( - - {props.initializeInProcess ? - - - : - - {props.isAuthorized ? -
: - - } - - } - + + + {props.initializeInProcess ? + + + : + + {props.isAuthorized ? +
: + + } + + } + + ); function mapStateToProps(state): Props { diff --git a/app/containers/IssueView/IssueView.jsx b/app/containers/IssueView/IssueView.jsx index ecace9235..8a1a0258d 100644 --- a/app/containers/IssueView/IssueView.jsx +++ b/app/containers/IssueView/IssueView.jsx @@ -19,9 +19,11 @@ import type { import { getUiState, getTimerState, + getSelectedIssue, } from 'selectors'; import { IssueViewPlaceholder, + ErrorBoundary, } from 'components'; import { uiActions, @@ -44,6 +46,7 @@ import IssueViewTabs from './IssueViewTabs'; type Props = { selectedIssueId: Id | null, + issueForDebug: any, currentTab: string, timerRunning: boolean, dispatch: Dispatch, @@ -58,48 +61,58 @@ const tabs = [ const IssueView: StatelessFunctionalComponent = ({ selectedIssueId, + issueForDebug, currentTab, timerRunning, dispatch, }: Props): Node => (selectedIssueId ? ( - - {timerRunning && - - } - - - { - dispatch(uiActions.setUiState('issueViewTab', tab)); - }} - currentTab={currentTab} - tabs={tabs} - /> - - {(() => { - switch (currentTab) { - case 'Details': - return ; - case 'Comments': - return ; - case 'Worklogs': - return ; - case 'Reports': - return ; - default: - return ; - } - })()} - - - + + + {timerRunning && + + } + + + { + dispatch(uiActions.setUiState('issueViewTab', tab)); + }} + currentTab={currentTab} + tabs={tabs} + /> + + {(() => { + switch (currentTab) { + case 'Details': + return ; + case 'Comments': + return ; + case 'Worklogs': + return ; + case 'Reports': + return ; + default: + return ; + } + })()} + + + + ) : ); function mapStateToProps(state) { return { selectedIssueId: getUiState('selectedIssueId')(state), + issueForDebug: getSelectedIssue(state), currentTab: getUiState('issueViewTab')(state), timerRunning: getTimerState('running')(state), }; diff --git a/app/containers/Sidebar/IssuesSourcePicker/IssuesSourcePicker.jsx b/app/containers/Sidebar/IssuesSourcePicker/IssuesSourcePicker.jsx index aaa2875a3..4704f25c3 100644 --- a/app/containers/Sidebar/IssuesSourcePicker/IssuesSourcePicker.jsx +++ b/app/containers/Sidebar/IssuesSourcePicker/IssuesSourcePicker.jsx @@ -75,6 +75,9 @@ const IssuesSourcePicker: StatelessFunctionalComponent = ({ dispatch(uiActions.setUiState('issuesSprintId', null)); dispatch(uiActions.setUiState('issuesSourceId', item.value)); dispatch(uiActions.setUiState('issuesSourceType', type)); + dispatch(uiActions.setIssuesFilters('assignee', [])); + dispatch(uiActions.setIssuesFilters('status', [])); + dispatch(uiActions.setIssuesFilters('type', [])); if (type === 'scrum') { dispatch(resourcesActions.clearResourceList({ resourceName: 'issues', diff --git a/app/containers/Sidebar/SidebarIssues/IssueItem/IssueItem.jsx b/app/containers/Sidebar/SidebarIssues/IssueItem/IssueItem.jsx index 3972d3b60..e1cefe902 100644 --- a/app/containers/Sidebar/SidebarIssues/IssueItem/IssueItem.jsx +++ b/app/containers/Sidebar/SidebarIssues/IssueItem/IssueItem.jsx @@ -68,31 +68,37 @@ const IssueItem: StatelessFunctionalComponent = ({ {issue.fields.summary} - - - - - - - - {issue.fields.status.name.toUpperCase()} - + {issue.fields.issuetype && + + + + } + {issue.fields.priority && + + + + } + {issue.fields.status && + + {issue.fields.status.name.toUpperCase()} + + } ; diff --git a/app/containers/Sidebar/SidebarIssues/SidebarIssues.jsx b/app/containers/Sidebar/SidebarIssues/SidebarIssues.jsx index 28be9c49c..964d1ef72 100644 --- a/app/containers/Sidebar/SidebarIssues/SidebarIssues.jsx +++ b/app/containers/Sidebar/SidebarIssues/SidebarIssues.jsx @@ -34,6 +34,7 @@ import { } from 'selectors'; import { IssueItemPlaceholder, + ErrorBoundary, } from 'components'; import { issuesActions, @@ -127,22 +128,24 @@ const SidebarAllItems: StatelessFunctionalComponent = ({ const item: ?Issue = issues[index]; return (
- {item ? - { - dispatch( - uiActions.setUiState('selectedIssueId', issueId), - ); - dispatch( - uiActions.setUiState('selectedWorklogId', null), - ); - }} - /> : - - } + + {item ? + { + dispatch( + uiActions.setUiState('selectedIssueId', issueId), + ); + dispatch( + uiActions.setUiState('selectedWorklogId', null), + ); + }} + /> : + + } +
); }} diff --git a/app/package.json b/app/package.json index f516f0f72..fadedd0d9 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "Chronos", "productName": "Chronos", - "version": "2.5.7", + "version": "2.5.8", "description": "Native app for time-tracking fully integrated with JIRA", "main": "./main.prod.js", "scripts": { diff --git a/app/sagas/issues.js b/app/sagas/issues.js index 558ee2c80..eead9fdc5 100644 --- a/app/sagas/issues.js +++ b/app/sagas/issues.js @@ -294,17 +294,18 @@ export function* fetchIssues({ }, }, ); + } else { + yield put(resourcesActions.setResourceMeta({ + resourceName: 'issues', + meta: { + filterIssuesTotalCount: 0, + }, + })); + yield put(actions.succeeded({ + resources: [], + })); + yield call(throwError, err); } - yield put(resourcesActions.setResourceMeta({ - resourceName: 'issues', - meta: { - filterIssuesTotalCount: 0, - }, - })); - yield put(actions.succeeded({ - resources: [], - })); - yield call(throwError, err); } } diff --git a/app/sagas/storage.js b/app/sagas/storage.js index 36a31f0a3..d9be8f9e1 100644 --- a/app/sagas/storage.js +++ b/app/sagas/storage.js @@ -15,7 +15,7 @@ const prefixedKeys = [ 'issuesSourceId', 'issuesSourceType', 'issuesSprintId', - 'issuesFilters', + 'issuesFiltersBySourceId', ]; export const storageGetPromise = (key: string): Promise => new Promise((resolve) => { diff --git a/package.json b/package.json index dd94c0659..4e532deab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Chronos", - "version": "2.5.7", + "version": "2.5.8", "description": "Full functionality time tracking software with direct JIRA integration", "scripts": { "build": "concurrently \"yarn build-main\" \"yarn build-renderer\"",