1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
import Select from 'react-select';
import { connect } from 'react-redux';
import differenceBy from 'lodash/differenceBy';
import ProjectNotifications from './ProjectNotifications';
import { translate } from '../../../helpers/l10n';
import { getComponents } from '../../../api/components';
import { getProjectsWithNotifications } from '../../../store/rootReducer';
type Props = {
projects: Array<{
key: string,
name: string
}>
};
type State = {
addedProjects: Array<{
key: string,
name: string
}>
};
class Projects extends React.Component {
props: Props;
state: State = {
addedProjects: []
};
componentWillReceiveProps (nextProps: Props) {
// remove all projects from `this.state.addedProjects` that already exist in `nextProps.projects`
const nextAddedProjects = differenceBy(
this.state.addedProjects,
nextProps.projects,
project => project.key
);
if (nextAddedProjects.length !== this.state.addedProjects) {
this.setState({ addedProjects: nextAddedProjects });
}
}
loadOptions = query => {
// TODO filter existing out
return getComponents({ qualifiers: 'TRK', q: query })
.then(r => r.components)
.then(projects => projects.map(project => ({
value: project.key,
label: project.name
})))
.then(options => ({ options }));
};
handleAddProject = selected => {
const project = { key: selected.value, name: selected.label };
this.setState({
addedProjects: [...this.state.addedProjects, project]
});
};
render () {
const allProjects = [...this.props.projects, ...this.state.addedProjects];
return (
<section>
<h2 className="spacer-bottom">
{translate('my_profile.per_project_notifications.title')}
</h2>
{allProjects.length === 0 && (
<div className="note">
{translate('my_account.no_project_notifications')}
</div>
)}
{allProjects.map(project => (
<ProjectNotifications key={project.key} project={project}/>
))}
<div className="spacer-top panel bg-muted">
<span className="text-middle spacer-right">
Set notifications for:
</span>
<Select.Async
name="new_project"
style={{ width: '300px' }}
loadOptions={this.loadOptions}
minimumInput={2}
onChange={this.handleAddProject}
placeholder="Search Project"
searchPromptText="Type at least 2 characters to search"/>
</div>
</section>
);
}
}
const mapStateToProps = state => ({
projects: getProjectsWithNotifications(state)
});
export default connect(mapStateToProps)(Projects);
export const UnconnectedProjects = Projects;
|