diff --git a/src/renderer/components/filters/AccountFilter.test.tsx b/src/renderer/components/filters/AccountFilter.test.tsx
new file mode 100644
index 000000000..bf0177b5c
--- /dev/null
+++ b/src/renderer/components/filters/AccountFilter.test.tsx
@@ -0,0 +1,25 @@
+import { renderWithProviders } from '../../__helpers__/test-utils';
+import { mockMultipleAccountNotifications } from '../../__mocks__/notifications-mocks';
+import { mockAuth } from '../../__mocks__/state-mocks';
+
+import { AccountFilter } from './AccountFilter';
+
+describe('renderer/components/filters/AccountFilter.tsx', () => {
+ it('should count notifications from the current filtered list per account', () => {
+ const tree = renderWithProviders(, {
+ accounts: mockAuth.accounts,
+ notifications: mockMultipleAccountNotifications,
+ });
+
+ const counters = Array.from(
+ tree.container.querySelectorAll('span.text-gitify-counter-text'),
+ ).map((el) => Number.parseInt(el.textContent ?? '0', 10));
+
+ expect(counters.reduce((sum, value) => sum + value, 0)).toBe(
+ mockMultipleAccountNotifications.reduce(
+ (sum, account) => sum + account.notifications.length,
+ 0,
+ ),
+ );
+ });
+});
diff --git a/src/renderer/components/filters/FilterSection.test.tsx b/src/renderer/components/filters/FilterSection.test.tsx
index c3c682b76..8a794ec90 100644
--- a/src/renderer/components/filters/FilterSection.test.tsx
+++ b/src/renderer/components/filters/FilterSection.test.tsx
@@ -4,7 +4,10 @@ import userEvent from '@testing-library/user-event';
import { MarkGithubIcon } from '@primer/octicons-react';
import { renderWithProviders } from '../../__helpers__/test-utils';
-import { mockMultipleAccountNotifications } from '../../__mocks__/notifications-mocks';
+import {
+ mockMultipleAccountNotifications,
+ mockPartialGitifyNotification,
+} from '../../__mocks__/notifications-mocks';
import { mockSettings } from '../../__mocks__/state-mocks';
import { useFiltersStore } from '../../stores';
@@ -116,4 +119,79 @@ describe('renderer/components/filters/FilterSection.tsx', () => {
expect(updateFilterSpy).toHaveBeenCalledWith(mockFilterSetting, 'closed', true);
});
+
+ it('should count notifications from the currently filtered list', () => {
+ const filteredNotifications = [
+ mockPartialGitifyNotification({ state: 'OPEN' }),
+ mockPartialGitifyNotification({ state: 'CLOSED' }),
+ mockPartialGitifyNotification({ state: 'MERGED' }),
+ ].map((notification) => ({
+ account: notification.account,
+ notifications: [notification],
+ error: null,
+ }));
+
+ renderWithProviders(
+ ,
+ {
+ settings: {
+ ...mockSettings,
+ detailedNotifications: true,
+ },
+ notifications: filteredNotifications,
+ },
+ );
+
+ const counters = getCounterValues();
+ expect(counters.reduce((sum, value) => sum + value, 0)).toBe(3);
+ });
+
+ it('should rescale counts when a filter in another section is toggled', () => {
+ renderWithProviders(
+ ,
+ {
+ settings: {
+ ...mockSettings,
+ detailedNotifications: true,
+ },
+ notifications: [
+ mockPartialGitifyNotification({ state: 'OPEN' }),
+ mockPartialGitifyNotification({ state: 'CLOSED' }),
+ mockPartialGitifyNotification({ state: 'MERGED' }),
+ ].map((notification) => ({
+ account: notification.account,
+ notifications: [notification],
+ error: null,
+ })),
+ },
+ );
+
+ const before = getCounterValues();
+ expect(before.reduce((sum, value) => sum + value, 0)).toBe(3);
+
+ act(() => {
+ useFiltersStore.getState().updateFilter('reasons', 'mention', true);
+ });
+
+ const after = getCounterValues();
+ expect(after).toEqual(before);
+ });
+
+ function getCounterValues(): number[] {
+ return Array.from(document.querySelectorAll('span.text-gitify-counter-text')).map((el) =>
+ Number.parseInt(el.textContent ?? '0', 10),
+ );
+ }
});
diff --git a/src/renderer/components/filters/FilterSection.tsx b/src/renderer/components/filters/FilterSection.tsx
index 9addf2d3c..f5f946071 100644
--- a/src/renderer/components/filters/FilterSection.tsx
+++ b/src/renderer/components/filters/FilterSection.tsx
@@ -66,12 +66,19 @@ const FilterSectionComponent = ({
{(Object.keys(filter.FILTER_TYPES) as FiltersState[K][number][])
- .sort((a, b) =>
- filter
+ .sort((a, b) => {
+ // Always pin the catch-all 'other' option to the end of the list.
+ if ((a as string) === 'other') {
+ return 1;
+ }
+ if ((b as string) === 'other') {
+ return -1;
+ }
+ return filter
.getTypeDetails(a)
.title.toLowerCase()
- .localeCompare(filter.getTypeDetails(b).title.toLowerCase(), 'en'),
- )
+ .localeCompare(filter.getTypeDetails(b).title.toLowerCase(), 'en');
+ })
.map((type) => {
const typeDetails = filter.getTypeDetails(type);
const typeTitle = typeDetails.title;
diff --git a/src/renderer/components/filters/ReviewRequestTypeFilter.tsx b/src/renderer/components/filters/ReviewRequestTypeFilter.tsx
index 0cae52ea3..cd30768fe 100644
--- a/src/renderer/components/filters/ReviewRequestTypeFilter.tsx
+++ b/src/renderer/components/filters/ReviewRequestTypeFilter.tsx
@@ -1,7 +1,7 @@
import type { FC } from 'react';
import { GitPullRequestIcon } from '@primer/octicons-react';
-import { Text } from '@primer/react';
+import { Stack, Text } from '@primer/react';
import { reviewRequestTypeFilter } from '../../utils/notifications/filters';
import { FilterSection } from './FilterSection';
@@ -15,9 +15,16 @@ export const ReviewRequestTypeFilter: FC = () => {
id="filter-review-request-types"
title="Review Request Type"
tooltip={
-
- Filter review requests by whether you were directly requested or requested via a team.
-
+
+
+ Filter review requests by whether you were directly requested or requested via a team.
+ Selecting an option shows only matching review requests.
+
+
+ Other shows all notifications that are not a review request, such as mentions, comments,
+ and assigned issues.
+
+
}
/>
);
diff --git a/src/renderer/components/filters/UserTypeFilter.tsx b/src/renderer/components/filters/UserTypeFilter.tsx
index 41a49b805..d6c09fcf2 100644
--- a/src/renderer/components/filters/UserTypeFilter.tsx
+++ b/src/renderer/components/filters/UserTypeFilter.tsx
@@ -20,7 +20,6 @@ export const UserTypeFilter: FC = () => {
filterSetting="userTypes"
icon={FeedPersonIcon}
id="filter-user-types"
- layout="horizontal"
title="User Type"
tooltip={
@@ -39,8 +38,15 @@ export const UserTypeFilter: FC = () => {
{userTypeFilter.FILTER_TYPES.Organization.title}
+
+
+ {userTypeFilter.FILTER_TYPES.other.title}
+
+
+ Other covers notifications with no author or a non-human, non-organization author.
+
}
/>
diff --git a/src/renderer/components/filters/__snapshots__/ReasonFilter.test.tsx.snap b/src/renderer/components/filters/__snapshots__/ReasonFilter.test.tsx.snap
index ab49792ac..93bd859fb 100644
--- a/src/renderer/components/filters/__snapshots__/ReasonFilter.test.tsx.snap
+++ b/src/renderer/components/filters/__snapshots__/ReasonFilter.test.tsx.snap
@@ -923,6 +923,62 @@ exports[`renderer/components/filters/ReasonFilter.tsx > should render itself & i
0
+
+
+
+
+
+ 0
+
+
diff --git a/src/renderer/components/filters/__snapshots__/ReviewRequestTypeFilter.test.tsx.snap b/src/renderer/components/filters/__snapshots__/ReviewRequestTypeFilter.test.tsx.snap
index 6add421b0..1f4341159 100644
--- a/src/renderer/components/filters/__snapshots__/ReviewRequestTypeFilter.test.tsx.snap
+++ b/src/renderer/components/filters/__snapshots__/ReviewRequestTypeFilter.test.tsx.snap
@@ -195,6 +195,62 @@ exports[`renderer/components/filters/ReviewRequestTypeFilter.tsx > should render
0
+
+
+
+
+
+ 4
+
+
diff --git a/src/renderer/components/filters/__snapshots__/SubjectTypeFilter.test.tsx.snap b/src/renderer/components/filters/__snapshots__/SubjectTypeFilter.test.tsx.snap
index 270e50710..abe9dfd74 100644
--- a/src/renderer/components/filters/__snapshots__/SubjectTypeFilter.test.tsx.snap
+++ b/src/renderer/components/filters/__snapshots__/SubjectTypeFilter.test.tsx.snap
@@ -460,6 +460,62 @@ exports[`renderer/components/filters/SubjectTypeFilter.tsx > should render itsel
0
+
+
+
+
+
+ 0
+
+
diff --git a/src/renderer/components/filters/__snapshots__/UserTypeFilter.test.tsx.snap b/src/renderer/components/filters/__snapshots__/UserTypeFilter.test.tsx.snap
index dd01ce90b..f3ef92d2c 100644
--- a/src/renderer/components/filters/__snapshots__/UserTypeFilter.test.tsx.snap
+++ b/src/renderer/components/filters/__snapshots__/UserTypeFilter.test.tsx.snap
@@ -77,8 +77,8 @@ exports[`renderer/components/filters/UserTypeFilter.tsx > should render itself &
class="prc-Stack-Stack-UQ9k6"
data-align="stretch"
data-component="Stack"
- data-direction="horizontal"
- data-gap="normal"
+ data-direction="vertical"
+ data-gap="condensed"
data-justify="start"
data-padding="none"
data-wrap="nowrap"
@@ -197,6 +197,62 @@ exports[`renderer/components/filters/UserTypeFilter.tsx > should render itself &
1
+
+
+
+
+
+ 3
+
+
diff --git a/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-dark-classic-chromium-linux.png b/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-dark-classic-chromium-linux.png
index 7ed6b856e..4be81f34d 100644
Binary files a/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-dark-classic-chromium-linux.png and b/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-dark-classic-chromium-linux.png differ
diff --git a/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-light-classic-chromium-linux.png b/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-light-classic-chromium-linux.png
index d7c63aa09..e6bbae9b4 100644
Binary files a/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-light-classic-chromium-linux.png and b/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-light-classic-chromium-linux.png differ
diff --git a/src/renderer/routes/__snapshots__/Filters.test.tsx.snap b/src/renderer/routes/__snapshots__/Filters.test.tsx.snap
index b0d980eb6..f39aa0060 100644
--- a/src/renderer/routes/__snapshots__/Filters.test.tsx.snap
+++ b/src/renderer/routes/__snapshots__/Filters.test.tsx.snap
@@ -561,8 +561,8 @@ exports[`renderer/routes/Filters.tsx > General > should render itself & its chil
class="prc-Stack-Stack-UQ9k6"
data-align="stretch"
data-component="Stack"
- data-direction="horizontal"
- data-gap="normal"
+ data-direction="vertical"
+ data-gap="condensed"
data-justify="start"
data-padding="none"
data-wrap="nowrap"
@@ -681,6 +681,62 @@ exports[`renderer/routes/Filters.tsx > General > should render itself & its chil
0
+
+
+
+
+
+ 0
+
+
@@ -2588,7 +2812,7 @@ exports[`renderer/routes/Filters.tsx > General > should render itself & its chil
data-wrap="nowrap"
>