Bug 1492270 - Convert list of pinned jobs to a Context

This commit is contained in:
Cameron Dawson 2018-09-18 13:38:26 -07:00
Родитель 8d5972d01d
Коммит 8aae70a82c
25 изменённых файлов: 390 добавлений и 326 удалений

Просмотреть файл

@ -7,19 +7,20 @@ const DIST = require('./base').DIST;
module.exports = neutrino => {
basePreset(neutrino);
neutrino.config.plugin('minify')
.inject(BabiliPlugin => new BabiliPlugin({
evaluate: false, // prevents some minification errors
// Prevents a minification error in react-dom that manifests as
// `ReferenceError: Hp is not defined` when loading the main jobs view (bug 1426902).
// TODO: Either remove this workaround or file upstream if this persists
// after the Neutrino upgrade (which comes with latest babel-plugin-minify-mangle-names).
mangle: {
keepFnName: true,
},
}
));
// neutrino.config.plugin('minify')
// .inject(BabiliPlugin => new BabiliPlugin({
// evaluate: false, // prevents some minification errors
// // Prevents a minification error in react-dom that manifests as
// // `ReferenceError: Hp is not defined` when loading the main jobs view (bug 1426902).
// // TODO: Either remove this workaround or file upstream if this persists
// // after the Neutrino upgrade (which comes with latest babel-plugin-minify-mangle-names).
// mangle: {
// keepFnName: true,
// },
// }
// ));
neutrino.config.plugins.delete('minify');
neutrino.config.devtool(false);
neutrino.config.plugin('clean')
.use(CleanPlugin, [DIST], { root: CWD } );

Просмотреть файл

@ -34,7 +34,7 @@ testpaths = tests
norecursedirs = __pycache__ ui
DJANGO_SETTINGS_MODULE=tests.settings
# Disable unused auto-loaded plugins.
addopts = --driver Firefox -p no:mozlog -p no:metadata -p no:html
addopts = --driver Firefox -p no:mozlog -p no:metadata
# Make most warnings fatal (including the hidden by default DeprecationWarning):
# https://docs.pytest.org/en/latest/warnings.html
# https://docs.python.org/2.7/library/warnings.html#warning-categories

Просмотреть файл

@ -13,7 +13,7 @@
font-size: 12px;
}
.pin-count-group {
#pin-count-group {
display: inline-block;
position: relative;
top: -14px;

Просмотреть файл

@ -5,6 +5,7 @@ import SplitPane from 'react-split-pane';
import treeherder from '../js/treeherder';
import { thEvents, thFavicons } from '../js/constants';
import { PinnedJobs } from './context/PinnedJobs';
import { matchesDefaults } from '../helpers/filter';
import { deployedRevisionUrl } from '../helpers/url';
import { getRepo } from '../helpers/location';
@ -74,9 +75,9 @@ class JobView extends React.Component {
this.toggleFieldFilterVisible = this.toggleFieldFilterVisible.bind(this);
this.updateDimensions = this.updateDimensions.bind(this);
this.pinJobs = this.pinJobs.bind(this);
this.setCurrentRepoTreeStatus = this.setCurrentRepoTreeStatus.bind(this);
this.handleUrlChanges = this.handleUrlChanges.bind(this);
this.selectFirstJob = this.selectFirstJob.bind(this);
RepositoryModel.getList().then((repos) => {
const currentRepo = repos.find(repo => repo.name === repoName) || this.state.currentRepo;
@ -95,7 +96,7 @@ class JobView extends React.Component {
window.addEventListener('resize', this.updateDimensions, false);
window.addEventListener('hashchange', this.handleUrlChanges, false);
this.$rootScope.$on(thEvents.toggleFieldFilterVisible, () => {
this.toggleFieldFilterVisibleUnlisten = this.$rootScope.$on(thEvents.toggleFieldFilterVisible, () => {
this.toggleFieldFilterVisible();
});
@ -128,6 +129,7 @@ class JobView extends React.Component {
}
componentWillUnmount() {
this.toggleFieldFilterVisibleUnlisten();
window.removeEventListener('resize', this.updateDimensions, false);
window.removeEventListener('hashchange', this.handleUrlChanges, false);
}
@ -178,10 +180,6 @@ class JobView extends React.Component {
this.setState({ isFieldFilterVisible: !this.state.isFieldFilterVisible });
}
pinJobs() {
this.$rootScope.$emit(thEvents.pinJobs, this.ThResultSetStore.getAllShownJobs());
}
updateDimensions() {
this.setState(JobView.getSplitterDimensions(this.props));
}
@ -192,6 +190,14 @@ class JobView extends React.Component {
});
}
selectFirstJob(jobs) {
const { selectedJob } = this.props;
if (!selectedJob) {
this.$rootScope.$emit(thEvents.jobClick, jobs[0]);
}
}
clearIfEligibleTarget(target) {
if (target.hasAttribute('data-job-clear-on-click')) {
this.$rootScope.$emit(thEvents.clearSelectedJob, target);
@ -230,64 +236,70 @@ class JobView extends React.Component {
), []);
return (
<KeyboardShortcuts
filterModel={filterModel}
<PinnedJobs
notify={this.thNotify}
selectedJob={selectedJob}
$injector={$injector}
selectFirstJob={this.selectFirstJob}
>
<PrimaryNavBar
repos={repos}
updateButtonClick={this.updateButtonClick}
pinJobs={this.pinJobs}
serverChanged={serverChanged}
<KeyboardShortcuts
filterModel={filterModel}
setUser={this.setUser}
user={user}
setCurrentRepoTreeStatus={this.setCurrentRepoTreeStatus}
selectedJob={selectedJob}
$injector={$injector}
/>
<SplitPane
split="horizontal"
size={`${pushListPct}%`}
onChange={size => this.handleSplitChange(size)}
>
<div className="d-flex flex-column w-100" onClick={evt => this.clearIfEligibleTarget(evt.target)}>
{(isFieldFilterVisible || !!filterBarFilters.length) && <ActiveFilters
$injector={$injector}
classificationTypes={classificationTypes}
filterModel={filterModel}
filterBarFilters={filterBarFilters}
isFieldFilterVisible={isFieldFilterVisible}
toggleFieldFilterVisible={this.toggleFieldFilterVisible}
/>}
{serverChangedDelayed && <UpdateAvailable
updateButtonClick={this.updateButtonClick}
/>}
<div id="th-global-content" className="th-global-content" data-job-clear-on-click>
<span className="th-view-content">
<PushList
user={user}
repoName={repoName}
revision={revision}
currentRepo={currentRepo}
filterModel={filterModel}
$injector={$injector}
/>
</span>
</div>
</div>
<DetailsPanel
resizedHeight={detailsHeight}
currentRepo={currentRepo}
repoName={repoName}
selectedJob={selectedJob}
<PrimaryNavBar
repos={repos}
updateButtonClick={this.updateButtonClick}
serverChanged={serverChanged}
filterModel={filterModel}
setUser={this.setUser}
user={user}
classificationTypes={classificationTypes}
classificationMap={classificationMap}
setCurrentRepoTreeStatus={this.setCurrentRepoTreeStatus}
$injector={$injector}
resultSetStore={this.ThResultSetStore}
/>
</SplitPane>
</KeyboardShortcuts>
<SplitPane
split="horizontal"
size={`${pushListPct}%`}
onChange={size => this.handleSplitChange(size)}
>
<div className="d-flex flex-column w-100" onClick={evt => this.clearIfEligibleTarget(evt.target)}>
{(isFieldFilterVisible || !!filterBarFilters.length) && <ActiveFilters
$injector={$injector}
classificationTypes={classificationTypes}
filterModel={filterModel}
filterBarFilters={filterBarFilters}
isFieldFilterVisible={isFieldFilterVisible}
toggleFieldFilterVisible={this.toggleFieldFilterVisible}
/>}
{serverChangedDelayed && <UpdateAvailable
updateButtonClick={this.updateButtonClick}
/>}
<div id="th-global-content" className="th-global-content" data-job-clear-on-click>
<span className="th-view-content">
<PushList
user={user}
repoName={repoName}
revision={revision}
currentRepo={currentRepo}
filterModel={filterModel}
$injector={$injector}
/>
</span>
</div>
</div>
<DetailsPanel
resizedHeight={detailsHeight}
currentRepo={currentRepo}
repoName={repoName}
selectedJob={selectedJob}
user={user}
classificationTypes={classificationTypes}
classificationMap={classificationMap}
$injector={$injector}
/>
</SplitPane>
</KeyboardShortcuts>
</PinnedJobs>
);
}
}

Просмотреть файл

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { HotKeys } from 'react-hotkeys';
import { thEvents, thJobNavSelectors } from '../js/constants';
import { withPinnedJobs } from './context/PinnedJobs';
const keyMap = {
addRelatedBug: 'b',
@ -27,7 +28,7 @@ const keyMap = {
deleteClassification: 'ctrl+backspace',
};
export default class KeyboardShortcuts extends React.Component {
class KeyboardShortcuts extends React.Component {
constructor(props) {
super(props);
@ -108,36 +109,37 @@ export default class KeyboardShortcuts extends React.Component {
// pin selected job to pinboard
pinJob() {
const { selectedJob } = this.props;
const { selectedJob, pinJob } = this.props;
if (selectedJob) {
this.$rootScope.$emit(thEvents.jobPin, selectedJob);
pinJob(selectedJob);
}
}
// pin selected job to pinboard and add a related bug
addRelatedBug() {
const { selectedJob } = this.$rootScope;
const { selectedJob, pinJob } = this.props;
if (selectedJob) {
this.$rootScope.$emit(thEvents.addRelatedBug, selectedJob);
pinJob(selectedJob);
document.getElementById('add-related-bug-button').click();
document.getElementById('related-bug-input').focus();
}
}
// pin selected job to pinboard and enter classification
pinEditComment() {
const { selectedJob } = this.$rootScope;
const { selectedJob, pinJob } = this.props;
if (selectedJob) {
this.$rootScope.$emit(thEvents.jobPin, selectedJob);
pinJob(selectedJob);
document.getElementById('classification-comment').focus();
}
}
// clear the PinBoard
clearPinboard() {
this.$rootScope.$emit(thEvents.clearPinboard);
this.props.unPinAll();
}
saveClassification() {
@ -261,6 +263,8 @@ export default class KeyboardShortcuts extends React.Component {
KeyboardShortcuts.propTypes = {
filterModel: PropTypes.object.isRequired,
$injector: PropTypes.object.isRequired,
pinJob: PropTypes.func.isRequired,
unPinAll: PropTypes.func.isRequired,
children: PropTypes.array.isRequired,
selectedJob: PropTypes.object,
};
@ -268,3 +272,5 @@ KeyboardShortcuts.propTypes = {
KeyboardShortcuts.defaultProps = {
selectedJob: null,
};
export default withPinnedJobs(KeyboardShortcuts);

Просмотреть файл

@ -0,0 +1,193 @@
import React from 'react';
import PropTypes from 'prop-types';
const COUNT_ERROR = 'Max pinboard size of 500 reached.';
const MAX_SIZE = 500;
const PinnedJobsContext = React.createContext({});
export class PinnedJobs extends React.Component {
constructor(props) {
super(props);
this.state = {
pinnedJobs: {},
pinnedJobBugs: {},
isPinBoardVisible: false,
};
this.value = {
...this.state,
setPinBoardVisible: this.setPinBoardVisible,
pinJob: this.pinJob,
unPinJob: this.unPinJob,
togglePinJob: this.togglePinJob,
pinJobs: this.pinJobs,
unPinAll: this.unPinAll,
addBug: this.addBug,
removeBug: this.removeBug,
};
}
componentDidMount() {
this.pinJob = this.pinJob.bind(this);
this.unPinJob = this.unPinJob.bind(this);
this.pinJobs = this.pinJobs.bind(this);
this.addBug = this.addBug.bind(this);
this.removeBug = this.removeBug.bind(this);
this.unPinAll = this.unPinAll.bind(this);
this.togglePinJob = this.togglePinJob.bind(this);
this.setPinBoardVisible = this.setPinBoardVisible.bind(this);
// TODO: this.value needs to now get the bound versions of the functions.
// But when we move the function binding to the constructors, we won't
// have to re-do this in componentDidMount.
this.value = {
...this.state,
setPinBoardVisible: this.setPinBoardVisible,
togglePinJob: this.togglePinJob,
pinJob: this.pinJob,
unPinJob: this.unPinJob,
pinJobs: this.pinJobs,
unPinAll: this.unPinAll,
addBug: this.addBug,
removeBug: this.removeBug,
};
}
setValue(newState, callback) {
this.value = { ...this.value, ...newState };
this.setState(newState, callback);
}
setPinBoardVisible(isPinBoardVisible) {
this.setValue({ isPinBoardVisible });
}
pinJob(job, callback) {
const { pinnedJobs } = this.state;
const { notify } = this.props;
if (MAX_SIZE - Object.keys(pinnedJobs).length > 0) {
this.setValue({
pinnedJobs: { ...pinnedJobs, [job.id]: job },
isPinBoardVisible: true,
}, () => { if (callback) callback(); });
this.pulsePinCount();
} else {
notify.send(COUNT_ERROR, 'danger');
}
}
unPinJob(id) {
const { pinnedJobs } = this.state;
delete pinnedJobs[id];
this.setValue({ pinnedJobs: { ...pinnedJobs } });
}
pinJobs(jobsToPin) {
const { pinnedJobs } = this.state;
const { notify, selectFirstJob } = this.props;
const spaceRemaining = MAX_SIZE - Object.keys(pinnedJobs).length;
const showError = jobsToPin.length > spaceRemaining;
const newPinnedJobs = jobsToPin.slice(0, spaceRemaining).reduce((acc, job) => ({ ...acc, [job.id]: job }), {});
if (!spaceRemaining) {
notify.send(COUNT_ERROR, 'danger', { sticky: true });
return;
}
this.setValue({
pinnedJobs: { ...pinnedJobs, ...newPinnedJobs },
isPinBoardVisible: true,
}, () => {
selectFirstJob(Object.values(newPinnedJobs));
if (showError) {
notify.send(COUNT_ERROR, 'danger', { sticky: true });
}
});
}
addBug(bug, job) {
const { pinnedJobBugs } = this.state;
pinnedJobBugs[bug.id] = bug;
this.setValue({ pinnedJobBugs: { ...pinnedJobBugs } });
if (job) {
this.pinJob(job);
}
}
removeBug(id) {
const { pinnedJobBugs } = this.state;
delete pinnedJobBugs[id];
this.setValue({ pinnedJobBugs: { ...pinnedJobBugs } });
}
unPinAll() {
this.setValue({
pinnedJobs: {},
pinnedJobBugs: {},
});
}
togglePinJob(job) {
const { pinnedJobs } = this.state;
if (pinnedJobs[job.id]) {
this.unPinJob(job.id);
} else {
this.pinJob(job);
}
}
pulsePinCount() {
const jobEl = document.getElementById('pin-count-group');
if (jobEl) {
jobEl.classList.add('pin-count-pulse');
window.setTimeout(() => {
jobEl.classList.remove('pin-count-pulse');
}, 700);
}
}
render() {
return (
<PinnedJobsContext.Provider value={this.value}>
{this.props.children}
</PinnedJobsContext.Provider>
);
}
}
export function withPinnedJobs(Component) {
return function PinBoardComponent(props) {
return (
<PinnedJobsContext.Consumer>
{context => (
<Component
{...props}
pinnedJobs={context.pinnedJobs}
pinnedJobBugs={context.pinnedJobBugs}
isPinBoardVisible={context.isPinBoardVisible}
setPinBoardVisible={context.setPinBoardVisible}
pinJob={context.pinJob}
unPinJob={context.unPinJob}
pinJobs={context.pinJobs}
unPinAll={context.unPinAll}
togglePinJob={context.togglePinJob}
addBug={context.addBug}
removeBug={context.removeBug}
/>
)}
</PinnedJobsContext.Consumer>
);
};
}
PinnedJobs.propTypes = {
notify: PropTypes.object.isRequired,
selectFirstJob: PropTypes.func.isRequired,
children: PropTypes.object.isRequired,
};

Просмотреть файл

@ -1,14 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { chunk } from 'lodash';
import $ from 'jquery';
import {
thEvents,
thBugSuggestionLimit,
thPinboardCountError,
thPinboardMaxSize,
} from '../../js/constants';
import { thEvents, thBugSuggestionLimit } from '../../js/constants';
import { withPinnedJobs } from '../context/PinnedJobs';
import { getLogViewerUrl, getReftestUrl } from '../../helpers/url';
import BugJobMapModel from '../../models/bugJobMap';
import BugSuggestionsModel from '../../models/bugSuggestions';
@ -17,7 +12,6 @@ import JobModel from '../../models/job';
import JobDetailModel from '../../models/jobDetail';
import JobLogUrlModel from '../../models/jobLogUrl';
import TextLogStepModel from '../../models/textLogStep';
import PinBoard from './PinBoard';
import SummaryPanel from './summary/SummaryPanel';
import TabsPanel from './tabs/TabsPanel';
@ -25,7 +19,7 @@ import { setUrlParam } from '../../helpers/location';
export const pinboardHeight = 100;
export default class DetailsPanel extends React.Component {
class DetailsPanel extends React.Component {
constructor(props) {
super(props);
@ -33,7 +27,6 @@ export default class DetailsPanel extends React.Component {
this.PhSeries = $injector.get('PhSeries');
this.ThResultSetStore = $injector.get('ThResultSetStore');
this.thNotify = $injector.get('thNotify');
this.$rootScope = $injector.get('$rootScope');
// used to cancel all the ajax requests triggered by selectJob
@ -41,7 +34,6 @@ export default class DetailsPanel extends React.Component {
this.state = {
job: null,
isPinBoardVisible: false,
jobDetails: [],
jobLogUrls: [],
jobDetailLoading: false,
@ -57,8 +49,6 @@ export default class DetailsPanel extends React.Component {
suggestions: [],
errors: [],
bugSuggestionsLoading: false,
pinnedJobs: {},
pinnedJobBugs: {},
};
}
@ -70,23 +60,13 @@ export default class DetailsPanel extends React.Component {
}
componentDidMount() {
this.pinJob = this.pinJob.bind(this);
this.unPinJob = this.unPinJob.bind(this);
this.unPinAll = this.unPinAll.bind(this);
this.addBug = this.addBug.bind(this);
this.removeBug = this.removeBug.bind(this);
this.closeJob = this.closeJob.bind(this);
this.countPinnedJobs = this.countPinnedJobs.bind(this);
// give access to this count to components that don't have a common ancestor in React
// TODO: remove this once pinnedJobs is converted to a model or Context
this.$rootScope.countPinnedJobs = this.countPinnedJobs;
this.jobClickUnlisten = this.$rootScope.$on(thEvents.jobClick, (evt, job) => {
this.setState({
jobDetailLoading: true,
jobDetails: [],
suggestions: [],
isPinBoardVisible: !!this.countPinnedJobs(),
}, () => this.selectJob(job));
});
@ -94,47 +74,20 @@ export default class DetailsPanel extends React.Component {
if (this.selectJobController !== null) {
this.selectJobController.abort();
}
if (!this.countPinnedJobs()) {
if (!Object.keys(this.props.pinnedJobs).length) {
this.closeJob();
}
});
this.toggleJobPinUnlisten = this.$rootScope.$on(thEvents.toggleJobPin, (event, job) => {
this.toggleJobPin(job);
});
this.jobPinUnlisten = this.$rootScope.$on(thEvents.jobPin, (event, job) => {
this.pinJob(job);
});
this.jobsClassifiedUnlisten = this.$rootScope.$on(thEvents.jobsClassified, () => {
this.updateClassifications(this.props.selectedJob);
});
this.pinAllShownJobsUnlisten = this.$rootScope.$on(thEvents.pinJobs, (event, jobs) => {
this.pinJobs(jobs);
});
this.clearPinboardUnlisten = this.$rootScope.$on(thEvents.clearPinboard, () => {
if (this.state.isPinBoardVisible) {
this.unPinAll();
}
});
this.pulsePinCountUnlisten = this.$rootScope.$on(thEvents.pulsePinCount, () => {
this.pulsePinCount();
});
}
componentWillUnmount() {
this.jobClickUnlisten();
this.clearSelectedJobUnlisten();
this.toggleJobPinUnlisten();
this.jobPinUnlisten();
this.jobsClassifiedUnlisten();
this.clearPinboardUnlisten();
this.pulsePinCountUnlisten();
this.pinAllShownJobsUnlisten();
}
getRevisionTips() {
@ -146,7 +99,9 @@ export default class DetailsPanel extends React.Component {
}
togglePinBoardVisibility() {
this.setState({ isPinBoardVisible: !this.state.isPinBoardVisible });
const { setPinBoardVisible, isPinBoardVisible } = this.props;
setPinBoardVisible(!isPinBoardVisible);
}
loadBugSuggestions(job) {
@ -319,108 +274,15 @@ export default class DetailsPanel extends React.Component {
this.setState({ isPinboardVisible: false });
}
toggleJobPin(job) {
const { pinnedJobs } = this.state;
if (pinnedJobs[job.id]) {
this.unPinJob(job.id);
} else {
this.pinJob(job);
}
}
pulsePinCount() {
$('.pin-count-group').addClass('pin-count-pulse');
window.setTimeout(() => {
$('.pin-count-group').removeClass('pin-count-pulse');
}, 700);
}
pinJob(job) {
const { pinnedJobs } = this.state;
if (thPinboardMaxSize - this.countPinnedJobs() > 0) {
this.setState({
pinnedJobs: { ...pinnedJobs, [job.id]: job },
isPinBoardVisible: true,
});
this.pulsePinCount();
} else {
this.thNotify.send(thPinboardCountError, 'danger');
}
if (!this.state.selectedJob) {
this.selectJob(job);
}
}
unPinJob(id) {
const { pinnedJobs } = this.state;
delete pinnedJobs[id];
this.setState({ pinnedJobs: { ...pinnedJobs } });
}
pinJobs(jobsToPin) {
const { pinnedJobs } = this.state;
const spaceRemaining = thPinboardMaxSize - this.countPinnedJobs();
const showError = jobsToPin.length > spaceRemaining;
const newPinnedJobs = jobsToPin.slice(0, spaceRemaining).reduce((acc, job) => ({ ...acc, [job.id]: job }), {});
if (!spaceRemaining) {
this.thNotify.send(thPinboardCountError, 'danger', { sticky: true });
return;
}
this.setState({
pinnedJobs: { ...pinnedJobs, ...newPinnedJobs },
isPinBoardVisible: true,
}, () => {
if (!this.props.selectedJob) {
this.$rootScope.$emit(thEvents.jobClick, jobsToPin[0]);
}
if (showError) {
this.thNotify.send(thPinboardCountError, 'danger', { sticky: true });
}
});
}
countPinnedJobs() {
return Object.keys(this.state.pinnedJobs).length;
}
addBug(bug, job) {
const { pinnedJobBugs } = this.state;
pinnedJobBugs[bug.id] = bug;
this.setState({ pinnedJobBugs: { ...pinnedJobBugs } });
if (job) {
this.pinJob(job);
}
}
removeBug(id) {
const { pinnedJobBugs } = this.state;
delete pinnedJobBugs[id];
this.setState({ pinnedJobBugs: { ...pinnedJobBugs } });
}
unPinAll() {
this.setState({
pinnedJobs: {},
pinnedJobBugs: {},
});
}
render() {
const {
repoName, $injector, user, currentRepo, resizedHeight, classificationMap,
classificationTypes,
classificationTypes, isPinBoardVisible,
} = this.props;
const {
job, isPinBoardVisible, jobDetails, jobRevision, jobLogUrls, jobDetailLoading,
job, jobDetails, jobRevision, jobLogUrls, jobDetailLoading,
perfJobDetail, suggestions, errors, bugSuggestionsLoading, logParseStatus,
classifications, logViewerUrl, logViewerFullUrl, pinnedJobs, pinnedJobBugs, bugs, reftestUrl,
classifications, logViewerUrl, logViewerFullUrl, bugs, reftestUrl,
} = this.state;
const detailsPanelHeight = isPinBoardVisible ? resizedHeight - pinboardHeight : resizedHeight;
@ -431,18 +293,10 @@ export default class DetailsPanel extends React.Component {
className={job ? 'details-panel-slide' : 'hidden'}
>
<PinBoard
isVisible={isPinBoardVisible}
selectedJob={job}
isLoggedIn={user.isLoggedIn || false}
classificationTypes={classificationTypes}
revisionList={this.getRevisionTips()}
pinnedJobs={pinnedJobs}
pinnedJobBugs={pinnedJobBugs}
addBug={this.addBug}
removeBug={this.removeBug}
pinJob={this.pinJob}
unPinJob={this.unPinJob}
unPinAll={this.unPinAll}
$injector={$injector}
/>
{!!job && <div id="details-panel-content">
@ -457,7 +311,6 @@ export default class DetailsPanel extends React.Component {
latestClassification={classifications.length ? classifications[0] : null}
logViewerUrl={logViewerUrl}
logViewerFullUrl={logViewerFullUrl}
pinJob={this.pinJob}
bugs={bugs}
user={user}
$injector={$injector}
@ -476,11 +329,7 @@ export default class DetailsPanel extends React.Component {
classifications={classifications}
classificationMap={classificationMap}
jobLogUrls={jobLogUrls}
isPinBoardVisible={isPinBoardVisible}
pinnedJobs={pinnedJobs}
bugs={bugs}
addBug={this.addBug}
pinJob={this.pinJob}
togglePinBoardVisibility={() => this.togglePinBoardVisibility()}
logViewerFullUrl={logViewerFullUrl}
reftestUrl={reftestUrl}
@ -502,9 +351,14 @@ DetailsPanel.propTypes = {
resizedHeight: PropTypes.number.isRequired,
classificationTypes: PropTypes.array.isRequired,
classificationMap: PropTypes.object.isRequired,
setPinBoardVisible: PropTypes.func.isRequired,
isPinBoardVisible: PropTypes.bool.isRequired,
pinnedJobs: PropTypes.object.isRequired,
selectedJob: PropTypes.object,
};
DetailsPanel.defaultProps = {
selectedJob: null,
};
export default withPinnedJobs(DetailsPanel);

Просмотреть файл

@ -10,8 +10,9 @@ import { getBugUrl } from '../../helpers/url';
import BugJobMapModel from '../../models/bugJobMap';
import JobClassificationModel from '../../models/classification';
import JobModel from '../../models/job';
import { withPinnedJobs } from '../context/PinnedJobs';
export default class PinBoard extends React.Component {
class PinBoard extends React.Component {
constructor(props) {
super(props);
@ -37,18 +38,12 @@ export default class PinBoard extends React.Component {
this.retriggerAllPinnedJobs = this.retriggerAllPinnedJobs.bind(this);
this.pasteSHA = this.pasteSHA.bind(this);
this.addRelatedBugUnlisten = this.$rootScope.$on(thEvents.addRelatedBug, (event, job) => {
this.props.pinJob(job);
this.toggleEnterBugNumber(true);
});
this.saveClassificationUnlisten = this.$rootScope.$on(thEvents.saveClassification, () => {
this.save();
});
}
componentWillUnmount() {
this.addRelatedBugUnlisten();
this.saveClassificationUnlisten();
}
@ -347,7 +342,7 @@ export default class PinBoard extends React.Component {
render() {
const {
selectedJob, revisionList, isLoggedIn, isVisible, classificationTypes,
selectedJob, revisionList, isLoggedIn, isPinBoardVisible, classificationTypes,
pinnedJobs, pinnedJobBugs, removeBug, unPinJob,
} = this.props;
const {
@ -359,7 +354,7 @@ export default class PinBoard extends React.Component {
return (
<div
id="pinboard-panel"
className={isVisible ? '' : 'hidden'}
className={isPinBoardVisible ? '' : 'hidden'}
>
<div id="pinboard-contents">
<div id="pinned-job-list">
@ -389,6 +384,7 @@ export default class PinBoard extends React.Component {
<div id="pinboard-related-bugs">
<div className="content">
<span
id="add-related-bug-button"
onClick={() => this.toggleEnterBugNumber(!enteringBugNumber)}
className="pointable"
title="Add a related bug"
@ -541,13 +537,12 @@ PinBoard.propTypes = {
$injector: PropTypes.object.isRequired,
classificationTypes: PropTypes.array.isRequired,
isLoggedIn: PropTypes.bool.isRequired,
isVisible: PropTypes.bool.isRequired,
isPinBoardVisible: PropTypes.bool.isRequired,
pinnedJobs: PropTypes.object.isRequired,
pinnedJobBugs: PropTypes.object.isRequired,
addBug: PropTypes.func.isRequired,
removeBug: PropTypes.func.isRequired,
unPinJob: PropTypes.func.isRequired,
pinJob: PropTypes.func.isRequired,
unPinAll: PropTypes.func.isRequired,
selectedJob: PropTypes.object,
email: PropTypes.string,
@ -559,3 +554,5 @@ PinBoard.defaultProps = {
email: null,
revisionList: [],
};
export default withPinnedJobs(PinBoard);

Просмотреть файл

@ -10,8 +10,9 @@ import JobModel from '../../../models/job';
import TaskclusterModel from '../../../models/taskcluster';
import CustomJobActions from '../../CustomJobActions';
import LogUrls from './LogUrls';
import { withPinnedJobs } from '../../context/PinnedJobs';
export default class ActionBar extends React.Component {
class ActionBar extends React.Component {
constructor(props) {
super(props);
@ -350,3 +351,5 @@ ActionBar.defaultProps = {
logViewerFullUrl: null,
jobLogUrls: [],
};
export default withPinnedJobs(ActionBar);

Просмотреть файл

@ -51,7 +51,7 @@ export default class SummaryPanel extends React.Component {
const {
repoName, selectedJob, latestClassification, bugs, jobLogUrls,
jobDetailLoading, buildUrl, logViewerUrl, logViewerFullUrl,
logParseStatus, pinJob, $injector, user, currentRepo, classificationMap,
logParseStatus, $injector, user, currentRepo, classificationMap,
} = this.props;
const { machineUrl, machineUrlStatus } = this.state;
@ -77,7 +77,6 @@ export default class SummaryPanel extends React.Component {
logViewerUrl={logViewerUrl}
logViewerFullUrl={logViewerFullUrl}
jobLogUrls={jobLogUrls}
pinJob={pinJob}
$injector={$injector}
user={user}
/>
@ -189,7 +188,6 @@ export default class SummaryPanel extends React.Component {
SummaryPanel.propTypes = {
repoName: PropTypes.string.isRequired,
pinJob: PropTypes.func.isRequired,
bugs: PropTypes.array.isRequired,
$injector: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,

Просмотреть файл

@ -12,8 +12,9 @@ import PerformanceTab from './PerformanceTab';
import AutoclassifyTab from './autoclassify/AutoclassifyTab';
import AnnotationsTab from './AnnotationsTab';
import SimilarJobsTab from './SimilarJobsTab';
import { withPinnedJobs } from '../../context/PinnedJobs';
export default class TabsPanel extends React.Component {
class TabsPanel extends React.Component {
constructor(props) {
super(props);
@ -92,9 +93,9 @@ export default class TabsPanel extends React.Component {
render() {
const {
jobDetails, jobLogUrls, logParseStatus, suggestions, errors, pinJob, user, bugs,
jobDetails, jobLogUrls, logParseStatus, suggestions, errors, user, bugs,
bugSuggestionsLoading, selectedJob, perfJobDetail, repoName, jobRevision,
classifications, togglePinBoardVisibility, isPinBoardVisible, pinnedJobs, addBug,
classifications, togglePinBoardVisibility, isPinBoardVisible, pinnedJobs,
classificationMap, logViewerFullUrl, reftestUrl, $injector,
} = this.props;
const { showAutoclassifyTab, tabIndex } = this.state;
@ -124,8 +125,9 @@ export default class TabsPanel extends React.Component {
title={isPinBoardVisible ? 'Close the pinboard' : 'Open the pinboard'}
>PinBoard
{!!countPinnedJobs && <div
id="pin-count-group"
title={`You have ${countPinnedJobs} job${countPinnedJobs > 1 ? 's' : ''} pinned`}
className={`pin-count-group ${countPinnedJobs > 99 ? 'pin-count-group-3-digit' : ''}`}
className={`${countPinnedJobs > 99 ? 'pin-count-group-3-digit' : ''}`}
>
<div
className={`pin-count-text ${countPinnedJobs > 99 ? 'pin-count-group-3-digit' : ''}`}
@ -152,8 +154,6 @@ export default class TabsPanel extends React.Component {
bugSuggestionsLoading={bugSuggestionsLoading}
jobLogUrls={jobLogUrls}
logParseStatus={logParseStatus}
addBug={addBug}
pinJob={pinJob}
logViewerFullUrl={logViewerFullUrl}
reftestUrl={reftestUrl}
$injector={$injector}
@ -165,9 +165,6 @@ export default class TabsPanel extends React.Component {
hasLogs={!!jobLogUrls.length}
logsParsed={logParseStatus !== 'pending'}
logParseStatus={logParseStatus}
addBug={addBug}
pinJob={pinJob}
pinnedJobs={pinnedJobs}
user={user}
$injector={$injector}
/>
@ -212,8 +209,6 @@ TabsPanel.propTypes = {
isPinBoardVisible: PropTypes.bool.isRequired,
pinnedJobs: PropTypes.object.isRequired,
bugs: PropTypes.array.isRequired,
addBug: PropTypes.func.isRequired,
pinJob: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
perfJobDetail: PropTypes.array,
suggestions: PropTypes.array,
@ -239,3 +234,5 @@ TabsPanel.defaultProps = {
logViewerFullUrl: null,
reftestUrl: null,
};
export default withPinnedJobs(TabsPanel);

Просмотреть файл

@ -9,8 +9,9 @@ import TextLogErrorsModel from '../../../../models/textLogErrors';
import AutoclassifyToolbar from './AutoclassifyToolbar';
import ErrorLine from './ErrorLine';
import ErrorLineData from './ErrorLineModel';
import { withPinnedJobs } from '../../../context/PinnedJobs';
export default class AutoclassifyTab extends React.Component {
class AutoclassifyTab extends React.Component {
constructor(props) {
super(props);
@ -459,7 +460,7 @@ export default class AutoclassifyTab extends React.Component {
}
render() {
const { job, autoclassifyStatus, user, $injector, addBug, pinnedJobs } = this.props;
const { job, autoclassifyStatus, user, $injector } = this.props;
const {
errorLines,
loadStatus,
@ -507,8 +508,6 @@ export default class AutoclassifyTab extends React.Component {
errorLine={errorLine}
prevErrorLine={errorLines[idx - 1]}
canClassify={canClassify}
addBug={addBug}
pinnedJobs={pinnedJobs}
$injector={$injector}
isSelected={selectedLineIds.has(errorLine.id)}
isEditable={editableLineIds.has(errorLine.id)}
@ -530,8 +529,6 @@ AutoclassifyTab.propTypes = {
job: PropTypes.object.isRequired,
hasLogs: PropTypes.bool.isRequired,
pinJob: PropTypes.func.isRequired,
addBug: PropTypes.func.isRequired,
pinnedJobs: PropTypes.object.isRequired,
autoclassifyStatus: PropTypes.string,
logsParsed: PropTypes.bool,
logParseStatus: PropTypes.string,
@ -542,3 +539,5 @@ AutoclassifyTab.defaultProps = {
logsParsed: false,
logParseStatus: 'pending',
};
export default withPinnedJobs(AutoclassifyTab);

Просмотреть файл

@ -489,7 +489,7 @@ export default class ErrorLine extends React.Component {
render() {
const {
errorLine, job, canClassify, isSelected, isEditable, setEditable,
$injector, toggleSelect, pinnedJobs, addBug,
$injector, toggleSelect,
} = this.props;
const {
messageExpanded, showHidden, selectedOption, options, extraOptions,
@ -592,8 +592,6 @@ export default class ErrorLine extends React.Component {
canClassify={canClassify}
onOptionChange={this.onOptionChange}
ignoreAlways={option.ignoreAlways}
pinnedJobs={pinnedJobs}
addBug={addBug}
$injector={$injector}
/>
</li>))}
@ -620,8 +618,6 @@ export default class ErrorLine extends React.Component {
manualBugNumber={option.manualBugNumber}
ignoreAlways={option.ignoreAlways}
$injector={$injector}
pinnedJobs={pinnedJobs}
addBug={addBug}
/>
</li>))}
</ul>}
@ -638,8 +634,6 @@ export default class ErrorLine extends React.Component {
setEditable={setEditable}
ignoreAlways={selectedOption.ignoreAlways}
manualBugNumber={selectedOption.manualBugNumber}
pinnedJobs={pinnedJobs}
addBug={addBug}
/>
</div>}
</div>
@ -658,8 +652,6 @@ ErrorLine.propTypes = {
setEditable: PropTypes.func.isRequired,
canClassify: PropTypes.bool.isRequired,
$injector: PropTypes.object.isRequired,
pinnedJobs: PropTypes.object.isRequired,
addBug: PropTypes.func.isRequired,
errorMatchers: PropTypes.object,
prevErrorLine: PropTypes.object,
};

Просмотреть файл

@ -10,11 +10,12 @@ import { getBugUrl, getLogViewerUrl, getReftestUrl } from '../../../../helpers/u
import BugFiler from '../../BugFiler';
import { thEvents } from '../../../../js/constants';
import { getAllUrlParams } from '../../../../helpers/location';
import { withPinnedJobs } from '../../../context/PinnedJobs';
/**
* Editable option
*/
export default class LineOption extends React.Component {
class LineOption extends React.Component {
constructor(props) {
super(props);
const { $injector } = props;
@ -212,3 +213,5 @@ LineOption.defaultProps = {
onIgnoreAlwaysChange: null,
manualBugNumber: undefined,
};
export default withPinnedJobs(LineOption);

Просмотреть файл

@ -4,11 +4,12 @@ import Highlighter from 'react-highlight-words';
import { getSearchWords } from '../../../../helpers/display';
import { getBugUrl } from '../../../../helpers/url';
import { withPinnedJobs } from '../../../context/PinnedJobs';
/**
* Non-editable best option
*/
export default function StaticLineOption(props) {
function StaticLineOption(props) {
const {
job, canClassify, errorLine, option, numOptions, setEditable, ignoreAlways,
manualBugNumber, pinnedJobs, addBug,
@ -91,3 +92,5 @@ StaticLineOption.propTypes = {
StaticLineOption.defaultProps = {
manualBugNumber: undefined,
};
export default withPinnedJobs(StaticLineOption);

Просмотреть файл

@ -4,9 +4,10 @@ import Highlighter from 'react-highlight-words';
import { getSearchWords } from '../../../../helpers/display';
import { getBugUrl } from '../../../../helpers/url';
import { withPinnedJobs } from '../../../context/PinnedJobs';
export default function BugListItem(props) {
function BugListItem(props) {
const {
bug, suggestion, bugClassName, title, selectedJob, addBug,
} = props;
@ -53,3 +54,5 @@ BugListItem.defaultProps = {
bugClassName: '',
title: null,
};
export default withPinnedJobs(BugListItem);

Просмотреть файл

@ -9,8 +9,9 @@ import ErrorsList from './ErrorsList';
import ListItem from './ListItem';
import SuggestionsListItem from './SuggestionsListItem';
import BugFiler from '../../BugFiler';
import { withPinnedJobs } from '../../../context/PinnedJobs';
export default class FailureSummaryTab extends React.Component {
class FailureSummaryTab extends React.Component {
constructor(props) {
super(props);
@ -54,7 +55,7 @@ export default class FailureSummaryTab extends React.Component {
render() {
const {
jobLogUrls, logParseStatus, suggestions, errors, logViewerFullUrl,
bugSuggestionsLoading, selectedJob, addBug, reftestUrl,
bugSuggestionsLoading, selectedJob, reftestUrl,
} = this.props;
const { isBugFilerOpen, suggestion } = this.state;
const logs = jobLogUrls;
@ -70,7 +71,6 @@ export default class FailureSummaryTab extends React.Component {
suggestion={suggestion}
toggleBugFiler={() => this.fileBug(suggestion)}
selectedJob={selectedJob}
addBug={addBug}
/>))}
{!!errors.length &&
@ -153,3 +153,5 @@ FailureSummaryTab.defaultProps = {
logParseStatus: 'pending',
logViewerFullUrl: null,
};
export default withPinnedJobs(FailureSummaryTab);

Просмотреть файл

@ -20,7 +20,7 @@ export default class SuggestionsListItem extends React.Component {
render() {
const {
suggestion, selectedJob, toggleBugFiler, addBug,
suggestion, selectedJob, toggleBugFiler,
} = this.props;
const { suggestionShowMore } = this.state;
@ -46,7 +46,6 @@ export default class SuggestionsListItem extends React.Component {
bug={bug}
selectedJob={selectedJob}
suggestion={suggestion}
addBug={addBug}
/>))}
</ul>}
@ -70,7 +69,6 @@ export default class SuggestionsListItem extends React.Component {
suggestion={suggestion}
bugClassName={bug.resolution !== '' ? 'deleted' : ''}
title={bug.resolution !== '' ? bug.resolution : ''}
addBug={addBug}
/>))}
</ul>}
@ -85,6 +83,5 @@ export default class SuggestionsListItem extends React.Component {
SuggestionsListItem.propTypes = {
suggestion: PropTypes.object.isRequired,
selectedJob: PropTypes.object.isRequired,
addBug: PropTypes.func.isRequired,
toggleBugFiler: PropTypes.func.isRequired,
};

Просмотреть файл

@ -2,11 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { thAllResultStatuses } from '../../js/constants';
import { withPinnedJobs } from '../context/PinnedJobs';
const resultStatusMenuItems = thAllResultStatuses.filter(rs => rs !== 'runnable');
export default function FiltersMenu(props) {
const { filterModel, pinJobs } = props;
function FiltersMenu(props) {
const { filterModel, pinJobs, resultSetStore } = props;
const { urlParams: { resultStatus, classifiedState } } = filterModel;
return (
@ -62,7 +63,7 @@ export default function FiltersMenu(props) {
<li
title="Pin all jobs that pass the global filters"
className="dropdown-item"
onClick={pinJobs}
onClick={() => pinJobs(resultSetStore.getAllShownJobs())}
>Pin all showing</li>
<li
title="Show only superseded jobs"
@ -83,4 +84,7 @@ export default function FiltersMenu(props) {
FiltersMenu.propTypes = {
filterModel: PropTypes.object.isRequired,
pinJobs: PropTypes.func.isRequired,
resultSetStore: PropTypes.object.isRequired,
};
export default withPinnedJobs(FiltersMenu);

Просмотреть файл

@ -13,8 +13,8 @@ import SecondaryNavBar from './SecondaryNavBar';
export default function PrimaryNavBar(props) {
const {
user, setUser, repos, pinJobs, updateButtonClick, serverChanged,
filterModel, $injector, setCurrentRepoTreeStatus,
user, setUser, repos, updateButtonClick, serverChanged,
filterModel, $injector, setCurrentRepoTreeStatus, resultSetStore,
} = props;
return (
@ -31,8 +31,8 @@ export default function PrimaryNavBar(props) {
filterModel={filterModel}
/>
<FiltersMenu
pinJobs={pinJobs}
filterModel={filterModel}
resultSetStore={resultSetStore}
/>
<HelpMenu />
<Login
@ -61,9 +61,9 @@ PrimaryNavBar.propTypes = {
filterModel: PropTypes.object.isRequired,
repos: PropTypes.array.isRequired,
updateButtonClick: PropTypes.func.isRequired,
pinJobs: PropTypes.func.isRequired,
serverChanged: PropTypes.bool.isRequired,
setUser: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
setCurrentRepoTreeStatus: PropTypes.func.isRequired,
resultSetStore: PropTypes.object.isRequired,
};

Просмотреть файл

@ -7,6 +7,7 @@ import { thEvents } from '../../js/constants';
import { getJobsUrl } from '../../helpers/url';
import PushModel from '../../models/push';
import JobModel from '../../models/job';
import { withPinnedJobs } from '../context/PinnedJobs';
// url params we don't want added from the current querystring to the revision
// and author links.
@ -58,7 +59,7 @@ PushCounts.propTypes = {
completed: PropTypes.number.isRequired,
};
export default class PushHeader extends React.PureComponent {
class PushHeader extends React.PureComponent {
constructor(props) {
super(props);
const { $injector, pushTimestamp } = this.props;
@ -69,9 +70,6 @@ export default class PushHeader extends React.PureComponent {
this.pushDateStr = toDateStr(pushTimestamp);
this.pinAllShownJobs = this.pinAllShownJobs.bind(this);
this.cancelAllJobs = this.cancelAllJobs.bind(this);
this.state = {
runnableJobsSelected: false,
};
@ -89,6 +87,11 @@ export default class PushHeader extends React.PureComponent {
);
}
componentDidMount() {
this.pinAllShownJobs = this.pinAllShownJobs.bind(this);
this.cancelAllJobs = this.cancelAllJobs.bind(this);
}
componentWillUnmount() {
this.toggleRunnableJobUnlisten();
}
@ -148,12 +151,10 @@ export default class PushHeader extends React.PureComponent {
}
pinAllShownJobs() {
const { pinJobs } = this.props;
const shownJobs = this.ThResultSetStore.getAllShownJobs(this.props.pushId);
this.$rootScope.$emit(thEvents.pinJobs, shownJobs);
if (!this.$rootScope.selectedJob) {
this.$rootScope.$emit(thEvents.jobClick, shownJobs[0]);
}
pinJobs(shownJobs);
}
render() {
@ -270,6 +271,7 @@ PushHeader.propTypes = {
hideRunnableJobsCb: PropTypes.func.isRequired,
cycleWatchState: PropTypes.func.isRequired,
isLoggedIn: PropTypes.bool.isRequired,
pinJobs: PropTypes.func.isRequired,
notificationSupported: PropTypes.bool.isRequired,
jobCounts: PropTypes.object,
watchState: PropTypes.string,
@ -279,3 +281,5 @@ PushHeader.defaultProps = {
jobCounts: null,
watchState: 'none',
};
export default withPinnedJobs(PushHeader);

Просмотреть файл

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { thPlatformMap, thSimplePlatforms, thEvents } from '../../js/constants';
import { withPinnedJobs } from '../context/PinnedJobs';
import { getPlatformRowId, getPushTableId } from '../../helpers/aggregateId';
import { findInstance, findSelectedInstance, findJobInstance } from '../../helpers/job';
import { getUrlParam } from '../../helpers/location';
@ -9,7 +10,7 @@ import { getLogViewerUrl } from '../../helpers/url';
import JobModel from '../../models/job';
import Platform from './Platform';
export default class PushJobs extends React.Component {
class PushJobs extends React.Component {
static getDerivedStateFromProps(nextProps, state) {
const { filterModel, push } = nextProps;
const { platforms } = state;
@ -104,6 +105,7 @@ export default class PushJobs extends React.Component {
}
onMouseDown(ev) {
const { togglePinJob } = this.props;
const jobElem = ev.target.attributes.getNamedItem('data-job-id');
if (jobElem) {
@ -116,7 +118,7 @@ export default class PushJobs extends React.Component {
this.ThResultSetStore.setSelectedJob(job);
this.selectJob(job, ev.target);
}
this.$rootScope.$emit(thEvents.toggleJobPin, job);
togglePinJob(job);
} else if (job.state === 'runnable') { // Toggle runnable
this.handleRunnableClick(job);
} else {
@ -234,5 +236,8 @@ PushJobs.propTypes = {
push: PropTypes.object.isRequired,
repoName: PropTypes.string.isRequired,
filterModel: PropTypes.object.isRequired,
togglePinJob: PropTypes.func.isRequired,
$injector: PropTypes.object.isRequired,
};
export default withPinnedJobs(PushJobs);

Просмотреть файл

@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import isEqual from 'lodash/isEqual';
import { thDefaultRepo, thEvents, thMaxPushFetchSize } from '../../js/constants';
import { withPinnedJobs } from '../context/PinnedJobs';
import { reloadOnChangeParameters } from '../../helpers/filter';
import {
findInstance,
@ -25,7 +26,7 @@ import ErrorBoundary from '../../shared/ErrorBoundary';
import Push from './Push';
import PushLoadErrors from './PushLoadErrors';
export default class PushList extends React.Component {
class PushList extends React.Component {
constructor(props) {
super(props);
const { $injector, repoName } = this.props;
@ -313,12 +314,15 @@ export default class PushList extends React.Component {
// Clear the selectedJob
closeJob() {
const { pinnedJobs } = this.props;
// TODO: Should block clearing the selected job if there are pinned jobs
// But can't get the pinned jobs at this time. When we're completely on React,
// or at least have a shared parent between PushList and DetailsPanel, we can share
// a PinBoardModel or Context so they both have access.
if (!this.$rootScope.countPinnedJobs()) {
if (!Object.keys(pinnedJobs).length) {
const selected = findSelectedInstance();
if (selected) {
selected.setSelected(false);
}
@ -386,6 +390,7 @@ PushList.propTypes = {
repoName: PropTypes.string.isRequired,
user: PropTypes.object.isRequired,
filterModel: PropTypes.object.isRequired,
pinnedJobs: PropTypes.object.isRequired,
revision: PropTypes.string,
currentRepo: PropTypes.object,
};
@ -394,3 +399,5 @@ PushList.defaultProps = {
revision: null,
currentRepo: {},
};
export default withPinnedJobs(PushList);

Просмотреть файл

@ -240,10 +240,6 @@ export const thJobNavSelectors = {
},
};
export const thPinboardCountError = 'Max pinboard size of 500 reached.';
export const thPinboardMaxSize = 500;
export const thPerformanceBranches = ['autoland', 'mozilla-inbound'];
/**
@ -254,12 +250,6 @@ export const thEvents = {
jobClick: 'job-click-EVT',
// fired with a selected job on 't'
selectNextTab: 'select-next-tab-EVT',
// fired with a selected job on spacebar
jobPin: 'job-pin-EVT',
// fired with a selected job on ctrl/cmd-click
toggleJobPin: 'job-togglepin-EVT',
// fired with api call to increment the pinned jobs
pulsePinCount: 'pulse-pin-count-EVT',
// fired with a selected job on 'r'
jobRetrigger: 'job-retrigger-EVT',
// fired when jobs are classified locally
@ -277,11 +267,8 @@ export const thEvents = {
showRunnableJobs: 'show-runnable-jobs-EVT',
deleteRunnableJobs: 'delete-runnable-jobs-EVT',
changeSelection: 'next-previous-job-EVT',
addRelatedBug: 'add-related-bug-EVT',
saveClassification: 'save-classification-EVT',
deleteClassification: 'delete-classification-EVT',
clearPinboard: 'clear-pinboard-EVT',
pinJobs: 'pin-jobs-EVT',
selectJob: 'select-job-EVT',
applyNewJobs: 'apply-new-jobs-EVT',
openLogviewer: 'open-logviewer-EVT',

Просмотреть файл

@ -22,9 +22,6 @@ treeherderApp.controller('MainCtrl', [
$rootScope.repoName = thDefaultRepo;
}
// TODO: Remove this when pinnedJobs is converted to a model or Context
$rootScope.countPinnedJobs = () => 0;
const getSingleRevisionTitleString = function () {
let revisions = [];
let percentComplete;