@@ -67,7 +67,6 @@ export type Props = { | |||
currentUser: CurrentUser, | |||
fetchIssues: (query: RawQuery) => Promise<*>, | |||
location: { pathname: string, query: RawQuery }, | |||
onRequestFail: Error => void, | |||
organization?: { key: string }, | |||
router: { | |||
push: ({ pathname: string, query?: RawQuery }) => void, | |||
@@ -381,27 +380,35 @@ export default class App extends React.PureComponent { | |||
fetchFirstIssues() { | |||
this.setState({ checked: [], loading: true }); | |||
return this.fetchIssues({}, true).then(({ facets, issues, paging, ...other }) => { | |||
if (this.mounted) { | |||
const openIssue = this.getOpenIssue(this.props, issues); | |||
this.setState({ | |||
facets: parseFacets(facets), | |||
loading: false, | |||
issues, | |||
openIssue, | |||
paging, | |||
referencedComponents: keyBy(other.components, 'uuid'), | |||
referencedLanguages: keyBy(other.languages, 'key'), | |||
referencedRules: keyBy(other.rules, 'key'), | |||
referencedUsers: keyBy(other.users, 'login'), | |||
selected: | |||
issues.length > 0 ? (openIssue != null ? openIssue.key : issues[0].key) : undefined, | |||
selectedFlowIndex: null, | |||
selectedLocationIndex: null | |||
}); | |||
return this.fetchIssues({}, true).then( | |||
({ facets, issues, paging, ...other }) => { | |||
if (this.mounted) { | |||
const openIssue = this.getOpenIssue(this.props, issues); | |||
this.setState({ | |||
facets: parseFacets(facets), | |||
loading: false, | |||
issues, | |||
openIssue, | |||
paging, | |||
referencedComponents: keyBy(other.components, 'uuid'), | |||
referencedLanguages: keyBy(other.languages, 'key'), | |||
referencedRules: keyBy(other.rules, 'key'), | |||
referencedUsers: keyBy(other.users, 'login'), | |||
selected: | |||
issues.length > 0 ? (openIssue != null ? openIssue.key : issues[0].key) : undefined, | |||
selectedFlowIndex: null, | |||
selectedLocationIndex: null | |||
}); | |||
} | |||
return issues; | |||
}, | |||
() => { | |||
if (this.mounted) { | |||
this.setState({ loading: false }); | |||
} | |||
return Promise.reject(); | |||
} | |||
return issues; | |||
}); | |||
); | |||
} | |||
fetchIssuesPage = (p /*: number */) => { | |||
@@ -433,15 +440,22 @@ export default class App extends React.PureComponent { | |||
const p = paging.pageIndex + 1; | |||
this.setState({ loading: true }); | |||
this.fetchIssuesPage(p).then(response => { | |||
if (this.mounted) { | |||
this.setState(state => ({ | |||
loading: false, | |||
issues: [...state.issues, ...response.issues], | |||
paging: response.paging | |||
})); | |||
this.fetchIssuesPage(p).then( | |||
response => { | |||
if (this.mounted) { | |||
this.setState(state => ({ | |||
loading: false, | |||
issues: [...state.issues, ...response.issues], | |||
paging: response.paging | |||
})); | |||
} | |||
}, | |||
() => { | |||
if (this.mounted) { | |||
this.setState({ loading: false }); | |||
} | |||
} | |||
}); | |||
); | |||
}; | |||
fetchIssuesForComponent = (component /*: string */, from /*: number */, to /*: number */) => { | |||
@@ -469,16 +483,24 @@ export default class App extends React.PureComponent { | |||
} | |||
this.setState({ loading: true }); | |||
return this.fetchIssuesUntil(paging.pageIndex + 1, done).then(response => { | |||
const nextIssues = [...issues, ...response.issues]; | |||
this.setState({ | |||
issues: nextIssues, | |||
loading: false, | |||
paging: response.paging | |||
}); | |||
return nextIssues.filter(isSameComponent); | |||
}); | |||
return this.fetchIssuesUntil(paging.pageIndex + 1, done).then( | |||
response => { | |||
const nextIssues = [...issues, ...response.issues]; | |||
if (this.mounted) { | |||
this.setState({ | |||
issues: nextIssues, | |||
loading: false, | |||
paging: response.paging | |||
}); | |||
} | |||
return nextIssues.filter(isSameComponent); | |||
}, | |||
() => { | |||
if (this.mounted) { | |||
this.setState({ loading: false }); | |||
} | |||
} | |||
); | |||
}; | |||
fetchFacet = (facet /*: string */) => { | |||
@@ -671,7 +693,6 @@ export default class App extends React.PureComponent { | |||
fetchIssues={bulkChange === 'all' ? this.fetchIssues : this.getCheckedIssues} | |||
onClose={this.closeBulkChange} | |||
onDone={this.handleBulkChangeDone} | |||
onRequestFail={this.props.onRequestFail} | |||
organization={this.props.organization} | |||
/>} | |||
</div> |
@@ -23,7 +23,7 @@ import { withRouter } from 'react-router'; | |||
/*:: import type { Dispatch } from 'redux'; */ | |||
import { uniq } from 'lodash'; | |||
import App from './App'; | |||
import { onFail } from '../../../store/rootActions'; | |||
import throwGlobalError from '../../../app/utils/throwGlobalError'; | |||
import { getComponent, getCurrentUser } from '../../../store/rootReducer'; | |||
import { getOrganizations } from '../../../api/organizations'; | |||
import { receiveOrganizations } from '../../../store/organizations/duck'; | |||
@@ -46,7 +46,7 @@ const fetchIssueOrganizations = issues => dispatch => { | |||
const organizationKeys = uniq(issues.map(issue => issue.organization)); | |||
return getOrganizations(organizationKeys).then( | |||
response => dispatch(receiveOrganizations(response.organizations)), | |||
onFail(dispatch) | |||
throwGlobalError | |||
); | |||
}; | |||
@@ -59,11 +59,8 @@ const fetchIssues = (query /*: RawQuery */) => dispatch => | |||
return { ...response, issues: parsedIssues }; | |||
}) | |||
.then(response => dispatch(fetchIssueOrganizations(response.issues)).then(() => response)) | |||
.catch(onFail(dispatch)); | |||
.catch(throwGlobalError); | |||
const onRequestFail = (error /*: Error */) => (dispatch /*: Dispatch<*> */) => | |||
onFail(dispatch)(error); | |||
const mapDispatchToProps = { fetchIssues, onRequestFail }; | |||
const mapDispatchToProps = { fetchIssues }; | |||
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(App)); |
@@ -29,6 +29,7 @@ import MarkdownTips from '../../../components/common/MarkdownTips'; | |||
import SeverityHelper from '../../../components/shared/SeverityHelper'; | |||
import Avatar from '../../../components/ui/Avatar'; | |||
import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; | |||
import throwGlobalError from '../../../app/utils/throwGlobalError'; | |||
import { searchIssueTags, bulkChangeIssues } from '../../../api/issues'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { searchAssignees } from '../utils'; | |||
@@ -42,7 +43,6 @@ type Props = {| | |||
fetchIssues: ({}) => Promise<*>, | |||
onClose: () => void, | |||
onDone: () => void, | |||
onRequestFail: Error => void, | |||
organization?: { key: string } | |||
|}; | |||
*/ | |||
@@ -200,7 +200,7 @@ export default class BulkChangeModal extends React.PureComponent { | |||
}, | |||
(error /*: Error */) => { | |||
this.setState({ submitting: false }); | |||
this.props.onRequestFail(error); | |||
throwGlobalError(error); | |||
} | |||
); | |||
}; |
@@ -24,13 +24,14 @@ import { formatMeasure } from '../../../helpers/measures'; | |||
/*:: | |||
type Props = { | |||
className? : string, | |||
current: ?number, | |||
total: number | |||
}; | |||
*/ | |||
const IssuesCounter = (props /*: Props */) => | |||
<span> | |||
<span className={props.className}> | |||
<strong> | |||
{props.current != null && | |||
<span> |
@@ -19,6 +19,7 @@ | |||
*/ | |||
// @flow | |||
import React from 'react'; | |||
import DeferredSpinner from '../../../components/common/DeferredSpinner'; | |||
import IssuesCounter from './IssuesCounter'; | |||
import ReloadButton from './ReloadButton'; | |||
/*:: import type { Paging } from '../utils'; */ | |||
@@ -62,10 +63,11 @@ export default class PageActions extends React.PureComponent { | |||
{this.renderShortcuts()} | |||
<div className="issues-page-actions"> | |||
{this.props.loading | |||
? <i className="issues-main-header-spinner spinner spacer-right" /> | |||
: <ReloadButton className="spacer-right" onClick={this.props.onReload} />} | |||
{paging != null && <IssuesCounter current={selectedIndex} total={paging.total} />} | |||
<DeferredSpinner className="issues-main-header-spinner" loading={this.props.loading}> | |||
<ReloadButton onClick={this.props.onReload} /> | |||
</DeferredSpinner> | |||
{paging != null && | |||
<IssuesCounter className="spacer-left" current={selectedIndex} total={paging.total} />} | |||
</div> | |||
</div> | |||
); |
@@ -2,6 +2,10 @@ | |||
line-height: 24px; | |||
} | |||
.issues-main-header-spinner { | |||
margin-right: 2px; | |||
} | |||
.concise-issues-list-header, | |||
.concise-issues-list-header-inner { | |||
} |