From c8195ef08cbec16490cdfd92f90114c10481a488 Mon Sep 17 00:00:00 2001
From: Adam Setch
Date: Thu, 29 Jan 2026 17:39:57 -0500
Subject: [PATCH 1/5] feat(metrics): add reaction metric pill
Signed-off-by: Adam Setch
---
.../components/metrics/MetricGroup.test.tsx | 60 +
.../components/metrics/MetricGroup.tsx | 72 +-
.../__snapshots__/MetricGroup.test.tsx.snap | 2259 ++++++++++++++++-
src/renderer/types.ts | 7 +
.../utils/api/__mocks__/response-mocks.ts | 60 +
src/renderer/utils/api/graphql/common.graphql | 7 +
.../utils/api/graphql/generated/gql.ts | 18 +-
.../utils/api/graphql/generated/graphql.ts | 170 +-
src/renderer/utils/api/graphql/issue.graphql | 12 +
src/renderer/utils/api/graphql/pull.graphql | 12 +
src/renderer/utils/api/graphql/utils.test.ts | 3 +-
.../notifications/handlers/issue.test.ts | 14 +
.../utils/notifications/handlers/issue.ts | 7 +
.../handlers/pullRequest.test.ts | 20 +
.../notifications/handlers/pullRequest.ts | 6 +
15 files changed, 2670 insertions(+), 57 deletions(-)
diff --git a/src/renderer/components/metrics/MetricGroup.test.tsx b/src/renderer/components/metrics/MetricGroup.test.tsx
index ade9d6c31..ef29c1552 100644
--- a/src/renderer/components/metrics/MetricGroup.test.tsx
+++ b/src/renderer/components/metrics/MetricGroup.test.tsx
@@ -48,6 +48,66 @@ describe('renderer/components/metrics/MetricGroup.tsx', () => {
});
});
+ describe('reactions pills', () => {
+ it('should render reaction pill when one reaction', async () => {
+ const mockNotification = mockGitifyNotification;
+ mockNotification.subject.reactionsCount = 1;
+ mockNotification.subject.reactionGroups = [
+ {
+ content: 'ROCKET',
+ reactors: {
+ totalCount: 1,
+ },
+ },
+ ];
+
+ const props: MetricGroupProps = {
+ notification: mockNotification,
+ };
+
+ const tree = renderWithAppContext();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should render reaction pill when multiple reactions', async () => {
+ const mockNotification = mockGitifyNotification;
+ mockNotification.subject.reactionsCount = 2;
+ mockNotification.subject.reactionGroups = [
+ {
+ content: 'THUMBS_UP',
+ reactors: {
+ totalCount: 1,
+ },
+ },
+ {
+ content: 'ROCKET',
+ reactors: {
+ totalCount: 1,
+ },
+ },
+ ];
+
+ const props: MetricGroupProps = {
+ notification: mockNotification,
+ };
+
+ const tree = renderWithAppContext();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should render issues pill when linked to multiple issues/prs', async () => {
+ const mockNotification = mockGitifyNotification;
+ mockNotification.subject.linkedIssues = ['#1', '#2'];
+
+ const props: MetricGroupProps = {
+ notification: mockNotification,
+ };
+
+ const tree = renderWithAppContext();
+ expect(tree).toMatchSnapshot();
+ });
+ });
+
describe('comment pills', () => {
it('should render when no comments', async () => {
const mockNotification = mockGitifyNotification;
diff --git a/src/renderer/components/metrics/MetricGroup.tsx b/src/renderer/components/metrics/MetricGroup.tsx
index 2cdd800fd..a02b54a20 100644
--- a/src/renderer/components/metrics/MetricGroup.tsx
+++ b/src/renderer/components/metrics/MetricGroup.tsx
@@ -4,6 +4,7 @@ import {
CommentIcon,
IssueOpenedIcon,
MilestoneIcon,
+ SmileyIcon,
TagIcon,
} from '@primer/octicons-react';
@@ -27,22 +28,72 @@ export const MetricGroup: FC = ({
const hasLinkedIssues = linkedIssues.length > 0;
const linkedIssuesPillDescription = hasLinkedIssues
? `Linked to ${
- linkedIssues.length > 1 ? 'issues' : 'issue'
+ linkedIssues.length > 1 ? 'issues:' : 'issue'
} ${linkedIssues.join(', ')}`
: '';
+ const reactionCount = notification.subject.reactionsCount ?? 0;
+ const reactionGroups = notification.subject.reactionGroups ?? [];
+ const hasReactions = reactionCount > 0;
+ const hasMultipleReactions =
+ reactionGroups.filter((rg) => rg.reactors.totalCount > 0).length > 1;
+ const reactionPillDescription = hasReactions
+ ? `${reactionCount} ${
+ reactionCount > 1 ? 'reactions' : 'reaction'
+ }: ${reactionGroups
+ .reduce((acc, rg) => {
+ if (!rg.reactors.totalCount || rg.reactors.totalCount <= 0) {
+ return acc;
+ }
+
+ let emoji = '';
+ switch (rg.content) {
+ case 'THUMBS_UP':
+ emoji = '👍';
+ break;
+ case 'THUMBS_DOWN':
+ emoji = '👎';
+ break;
+ case 'LAUGH':
+ emoji = '😆';
+ break;
+ case 'HOORAY':
+ emoji = '🎉';
+ break;
+ case 'CONFUSED':
+ emoji = '😕';
+ break;
+ case 'ROCKET':
+ emoji = '🚀';
+ break;
+ case 'EYES':
+ emoji = '👀';
+ break;
+ case 'HEART':
+ emoji = '❤️';
+ break;
+ default:
+ return acc;
+ }
+ acc.push(
+ `${emoji} ${hasMultipleReactions ? rg.reactors.totalCount : ''}`.trim(),
+ );
+ return acc;
+ }, [] as string[])
+ .join(' ')}`
+ : '';
+
const commentCount = notification.subject.commentCount ?? 0;
const hasComments = commentCount > 0;
const commentsPillDescription = hasComments
- ? `${notification.subject.commentCount} ${
- notification.subject.commentCount > 1 ? 'comments' : 'comment'
- }`
+ ? `${notification.subject.commentCount} ${notification.subject.commentCount > 1 ? 'comments' : 'comment'}`
: '';
const labels = notification.subject.labels ?? [];
- const hasLabels = labels.length > 0;
+ const labelsCount = labels.length;
+ const hasLabels = labelsCount > 0;
const labelsPillDescription = hasLabels
- ? labels.map((label) => `🏷️ ${label}`).join(', ')
+ ? `${labelsCount} ${labelsCount > 1 ? 'labels' : 'label'}: ${labels.map((label) => `🏷️ ${label}`).join(', ')}`
: '';
const milestone = notification.subject.milestone;
@@ -59,6 +110,15 @@ export const MetricGroup: FC = ({
/>
)}
+ {hasReactions && (
+
+ )}
+
{notification.subject.reviews?.map((review) => {
const icon = getPullRequestReviewIcon(review);
if (!icon) {
diff --git a/src/renderer/components/metrics/__snapshots__/MetricGroup.test.tsx.snap b/src/renderer/components/metrics/__snapshots__/MetricGroup.test.tsx.snap
index c632a7e5b..1f97bbd52 100644
--- a/src/renderer/components/metrics/__snapshots__/MetricGroup.test.tsx.snap
+++ b/src/renderer/components/metrics/__snapshots__/MetricGroup.test.tsx.snap
@@ -71,7 +71,60 @@ exports[`renderer/components/metrics/MetricGroup.tsx comment pills should render
popover="auto"
role="tooltip"
>
- Linked to issues #1, #2
+ Linked to issues: #1, #2
+
+
+
+ 2 reactions: 👍 1 🚀 1
+
+
+
+
+
+
+
+
+
+
+ Linked to issues: #1, #2
+
+
+
+
+
+
+
+ 2 reactions: 👍 1 🚀 1
+
+
+
+
+
+
+
+ octocat approved these changes
+
+
+
+
+
+
+
+ gitify-app requested changes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Linked to issues: #1, #2
+
+
+
+
+
+
+
+ 1 reaction: 🚀
+
+
+
+
+
+
+
+ octocat approved these changes
+
+
+
+
+
+
+
+ gitify-app requested changes
+
+
+
+
+
+