diff --git a/webapp/src/components/sidebar_right/sidebar_right.jsx b/webapp/src/components/sidebar_right/sidebar_right.jsx index 567527929..5f654f34c 100644 --- a/webapp/src/components/sidebar_right/sidebar_right.jsx +++ b/webapp/src/components/sidebar_right/sidebar_right.jsx @@ -4,8 +4,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import Scrollbars from 'react-custom-scrollbars-2'; +import ReactSelect from 'react-select'; + +import {makeStyleFromTheme} from 'mattermost-redux/utils/theme_utils'; import {RHSStates} from '../../constants'; +import {getStyleForReactSelect} from '@/utils/styles'; import GithubItems from './github_items'; @@ -43,6 +47,13 @@ function mapGithubItemListToPrList(gilist) { }); } +function getRepoName(item) { + if (item.repository_url) { + return item.repository_url.replace(/.+\/repos\//, ''); + } + return item.repository?.full_name ?? null; +} + function shouldUpdateDetails(prs, prevPrs, targetState, currentState, prevState) { if (currentState === targetState) { if (currentState !== prevState) { @@ -81,6 +92,15 @@ export default class SidebarRight extends React.PureComponent { }).isRequired, }; + constructor(props) { + super(props); + this.state = {selectedRepo: ''}; + } + + handleRepoFilterChange = (selectedOption) => { + this.setState({selectedRepo: selectedOption ? selectedOption.value : ''}); + } + componentDidMount() { if (this.props.yourPrs && this.props.rhsState === RHSStates.PRS) { this.props.actions.getYourPrsDetails(mapGithubItemListToPrList(this.props.yourPrs)); @@ -92,6 +112,22 @@ export default class SidebarRight extends React.PureComponent { } componentDidUpdate(prevProps) { + // Reset repo filter when switching RHS tabs + if (prevProps.rhsState !== this.props.rhsState) { + this.setState({selectedRepo: ''}); // eslint-disable-line react/no-did-update-set-state + } + + // Validate selectedRepo against the current list — reset if the repo + // is no longer present (stale state when the underlying data changes) + if (this.state.selectedRepo) { + const currentRepos = [...new Set( + this.getCurrentItems().map(getRepoName).filter(Boolean), + )]; + if (!currentRepos.includes(this.state.selectedRepo)) { + this.setState({selectedRepo: ''}); // eslint-disable-line react/no-did-update-set-state + } + } + if (shouldUpdateDetails(this.props.yourPrs, prevProps.yourPrs, RHSStates.PRS, this.props.rhsState, prevProps.rhsState)) { this.props.actions.getYourPrsDetails(mapGithubItemListToPrList(this.props.yourPrs)); } @@ -101,6 +137,22 @@ export default class SidebarRight extends React.PureComponent { } } + getCurrentItems = () => { + const {yourPrs, reviews, unreads, yourAssignments, rhsState} = this.props; + switch (rhsState) { + case RHSStates.PRS: + return yourPrs || []; + case RHSStates.REVIEWS: + return reviews || []; + case RHSStates.UNREADS: + return unreads || []; + case RHSStates.ASSIGNMENTS: + return yourAssignments || []; + default: + return []; + } + } + render() { const baseURL = this.props.enterpriseURL ? this.props.enterpriseURL : 'https://github.com'; let orgQuery = ''; @@ -134,17 +186,39 @@ export default class SidebarRight extends React.PureComponent { githubItems = unreads; title = 'Unread Messages'; listUrl = baseURL + '/notifications'; + break; case RHSStates.ASSIGNMENTS: githubItems = yourAssignments; title = 'Your Assignments'; listUrl = baseURL + '/pulls?q=is%3Aopen+archived%3Afalse+assignee%3A' + username + orgQuery; + break; default: break; } + // Extract unique repo names for filter dropdown + const repoNames = [...new Set( + githubItems.map(getRepoName).filter(Boolean), + )].sort(); + + // Build react-select options + const repoOptions = [ + {value: '', label: 'All repositories'}, + ...repoNames.map((repo) => ({value: repo, label: repo})), + ]; + + // Filter items by selected repo + const {selectedRepo} = this.state; + const selectedOption = repoOptions.find((opt) => opt.value === selectedRepo) || repoOptions[0]; + const filteredItems = selectedRepo ? + githubItems.filter((item) => getRepoName(item) === selectedRepo) : + githubItems; + + const style = getStyle(this.props.theme); + return ( {title} + {repoNames.length > 1 && ( +
+ +
+ )}
{ + return { + sectionHeader: { + padding: '15px', + }, + repoFilterContainer: { + marginLeft: '8px', + minWidth: '160px', + maxWidth: '220px', + fontSize: '12px', + }, + }; +});