Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion webapp/src/components/sidebar_right/sidebar_right.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ export default class SidebarRight extends React.PureComponent {
}).isRequired,
};

constructor(props) {
super(props);
this.state = {selectedRepo: ''};
}

handleRepoFilterChange = (e) => {
this.setState({selectedRepo: e.target.value});
}

componentDidMount() {
if (this.props.yourPrs && this.props.rhsState === RHSStates.PRS) {
this.props.actions.getYourPrsDetails(mapGithubItemListToPrList(this.props.yourPrs));
Expand All @@ -92,6 +101,11 @@ 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: ''});
}

if (shouldUpdateDetails(this.props.yourPrs, prevProps.yourPrs, RHSStates.PRS, this.props.rhsState, prevProps.rhsState)) {
this.props.actions.getYourPrsDetails(mapGithubItemListToPrList(this.props.yourPrs));
}
Expand Down Expand Up @@ -134,17 +148,42 @@ 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(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repo-name extraction logic is duplicated. Please factor it into a small helper at module scope so both call sites stay in sync if the GitHub response shape ever changes.

function getRepoName(item) {
    if (item.repository_url) {
        return item.repository_url.replace(/.+\/repos\//, '');
    }
    return item.repository?.full_name ?? null;
}

githubItems.map((item) => {
if (item.repository_url) {
return item.repository_url.replace(/.+\/repos\//, '');
} else if (item.repository?.full_name) {
return item.repository.full_name;
}
return null;
}).filter(Boolean),
)].sort();

// Filter items by selected repo
const {selectedRepo} = this.state;
const filteredItems = selectedRepo ?
githubItems.filter((item) => {
const repoName = item.repository_url ?
item.repository_url.replace(/.+\/repos\//, '') :
item.repository?.full_name;
return repoName === selectedRepo;
}) :
githubItems;

return (
<React.Fragment>
<Scrollbars
Expand All @@ -163,10 +202,22 @@ export default class SidebarRight extends React.PureComponent {
rel='noopener noreferrer'
>{title}</a>
</strong>
{repoNames.length > 1 && (
<select

@nang2049 nang2049 Jun 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs an aria-label='Filter by repository'. Also I worry that a native will look out of place next to Mattermost's styled UI. Other plugin dropdowns use react-select. Perhaps switch to that one ? Also since sectionHeader uses default inline layout, adding the next to a long title may wrap awkwardly on narrow RHS widths.

value={selectedRepo}
onChange={this.handleRepoFilterChange}
style={style.repoFilter}
>
<option value=''>All repositories</option>
{repoNames.map((repo) => (
<option key={repo} value={repo}>{repo}</option>
))}
</select>
)}
</div>
<div>
<GithubItems
items={githubItems}
items={filteredItems}
theme={this.props.theme}
showReviewSLA={rhsState === RHSStates.REVIEWS}
reviewTargetDays={this.props.reviewTargetDays || 0}
Expand All @@ -182,4 +233,12 @@ const style = {
sectionHeader: {
padding: '15px',
},
repoFilter: {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard-coded border will be invisible on dark themes and the transparent background will inherit the dark RHS color leaving the option text unreadable. This plugin already has a convention for theme-aware styles via makeStyleFromTheme, see sidebar_buttons.jsx:

import {makeStyleFromTheme, changeOpacity} from 'mattermost-redux/utils/theme_utils';
// ... bottom of file, replacing the plain `style` object:
const getStyle = makeStyleFromTheme((theme) => ({
    sectionHeader: {
        padding: '15px',
    },
    repoFilter: {
        marginLeft: '8px',
        padding: '2px 4px',
        fontSize: '12px',
        borderRadius: '4px',
        border: `1px solid ${changeOpacity(theme.centerChannelColor, 0.2)}`,
        background: 'transparent',
        color: theme.centerChannelColor,
    },
}));

marginLeft: '8px',
padding: '2px 4px',
fontSize: '12px',
borderRadius: '4px',
border: '1px solid rgba(0, 0, 0, 0.2)',
background: 'transparent',
},
};