diff --git a/src/renderer/components/avatars/AvatarWithFallback.test.tsx b/src/renderer/components/avatars/AvatarWithFallback.test.tsx
new file mode 100644
index 000000000..b94762e47
--- /dev/null
+++ b/src/renderer/components/avatars/AvatarWithFallback.test.tsx
@@ -0,0 +1,67 @@
+import { render, screen } from '@testing-library/react';
+
+import { type Link, Size } from '../../types';
+import {
+ AvatarWithFallback,
+ type IAvatarWithFallback,
+} from './AvatarWithFallback';
+
+describe('renderer/components/avatars/AvatarWithFallback.tsx', () => {
+ const props: IAvatarWithFallback = {
+ src: 'https://avatars.githubusercontent.com/u/133795385?s=200&v=4' as Link,
+ alt: 'gitify-app',
+ name: '@gitify-app',
+ size: Size.MEDIUM,
+ userType: 'User',
+ };
+
+ it('should render avatar - human user', () => {
+ const tree = render( );
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should render avatar - non-human user', () => {
+ const tree = render(
+ ,
+ );
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders the fallback icon when no src url - human user', () => {
+ const tree = render( );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders the fallback icon when no src url - non human user', () => {
+ const tree = render(
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('renders the fallback icon when the image fails to load (isBroken = true) - human user', () => {
+ render( );
+
+ // Find the avatar element by its alt text
+ const avatar = screen.getByAltText('gitify-app') as HTMLImageElement;
+
+ // Simulate an error event on the image element
+ avatar.dispatchEvent(new Event('error'));
+
+ expect(screen.getByTestId('avatar')).toMatchSnapshot();
+ });
+
+ it('renders the fallback icon when the image fails to load (isBroken = true) - non human user', () => {
+ render( );
+
+ // Find the avatar element by its alt text
+ const avatar = screen.getByAltText('gitify-app') as HTMLImageElement;
+
+ // Simulate an error event on the image element
+ avatar.dispatchEvent(new Event('error'));
+
+ expect(screen.getByTestId('avatar')).toMatchSnapshot();
+ });
+});
diff --git a/src/renderer/components/avatars/AvatarWithFallback.tsx b/src/renderer/components/avatars/AvatarWithFallback.tsx
new file mode 100644
index 000000000..8610c922e
--- /dev/null
+++ b/src/renderer/components/avatars/AvatarWithFallback.tsx
@@ -0,0 +1,54 @@
+import type React from 'react';
+import { useState } from 'react';
+
+import { FeedPersonIcon, MarkGithubIcon } from '@primer/octicons-react';
+import { Avatar, Stack, Text } from '@primer/react';
+
+import { type Link, Size } from '../../types';
+import type { UserType } from '../../typesGitHub';
+import { isNonHumanUser } from '../../utils/helpers';
+
+export interface IAvatarWithFallback {
+ src?: Link;
+ alt?: string;
+ name?: string;
+ size?: number;
+ userType?: UserType;
+}
+
+export const AvatarWithFallback: React.FC = ({
+ src,
+ alt,
+ name,
+ size = Size.MEDIUM,
+ userType = 'User',
+}) => {
+ const [isBroken, setIsBroken] = useState(false);
+
+ const isNonHuman = isNonHumanUser(userType);
+ return (
+
+ {!src || isBroken ? (
+ isNonHuman ? (
+
+ ) : (
+
+ )
+ ) : (
+ setIsBroken(true)}
+ />
+ )}
+ {name && {name} }
+
+ );
+};
diff --git a/src/renderer/components/avatars/__snapshots__/AvatarWithFallback.test.tsx.snap b/src/renderer/components/avatars/__snapshots__/AvatarWithFallback.test.tsx.snap
new file mode 100644
index 000000000..16d16b195
--- /dev/null
+++ b/src/renderer/components/avatars/__snapshots__/AvatarWithFallback.test.tsx.snap
@@ -0,0 +1,543 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renderer/components/avatars/AvatarWithFallback.tsx renders the fallback icon when no src url - human user 1`] = `
+{
+ "asFragment": [Function],
+ "baseElement":
+
+
+
+
+
+
+ @gitify-app
+
+
+
+ ,
+ "container":
+
+
+
+
+
+ @gitify-app
+
+
+
,
+ "debug": [Function],
+ "findAllByAltText": [Function],
+ "findAllByDisplayValue": [Function],
+ "findAllByLabelText": [Function],
+ "findAllByPlaceholderText": [Function],
+ "findAllByRole": [Function],
+ "findAllByTestId": [Function],
+ "findAllByText": [Function],
+ "findAllByTitle": [Function],
+ "findByAltText": [Function],
+ "findByDisplayValue": [Function],
+ "findByLabelText": [Function],
+ "findByPlaceholderText": [Function],
+ "findByRole": [Function],
+ "findByTestId": [Function],
+ "findByText": [Function],
+ "findByTitle": [Function],
+ "getAllByAltText": [Function],
+ "getAllByDisplayValue": [Function],
+ "getAllByLabelText": [Function],
+ "getAllByPlaceholderText": [Function],
+ "getAllByRole": [Function],
+ "getAllByTestId": [Function],
+ "getAllByText": [Function],
+ "getAllByTitle": [Function],
+ "getByAltText": [Function],
+ "getByDisplayValue": [Function],
+ "getByLabelText": [Function],
+ "getByPlaceholderText": [Function],
+ "getByRole": [Function],
+ "getByTestId": [Function],
+ "getByText": [Function],
+ "getByTitle": [Function],
+ "queryAllByAltText": [Function],
+ "queryAllByDisplayValue": [Function],
+ "queryAllByLabelText": [Function],
+ "queryAllByPlaceholderText": [Function],
+ "queryAllByRole": [Function],
+ "queryAllByTestId": [Function],
+ "queryAllByText": [Function],
+ "queryAllByTitle": [Function],
+ "queryByAltText": [Function],
+ "queryByDisplayValue": [Function],
+ "queryByLabelText": [Function],
+ "queryByPlaceholderText": [Function],
+ "queryByRole": [Function],
+ "queryByTestId": [Function],
+ "queryByText": [Function],
+ "queryByTitle": [Function],
+ "rerender": [Function],
+ "unmount": [Function],
+}
+`;
+
+exports[`renderer/components/avatars/AvatarWithFallback.tsx renders the fallback icon when no src url - non human user 1`] = `
+{
+ "asFragment": [Function],
+ "baseElement":
+
+
+
+
+
+
+ @gitify-app
+
+
+
+ ,
+ "container":
+
+
+
+
+
+ @gitify-app
+
+
+
,
+ "debug": [Function],
+ "findAllByAltText": [Function],
+ "findAllByDisplayValue": [Function],
+ "findAllByLabelText": [Function],
+ "findAllByPlaceholderText": [Function],
+ "findAllByRole": [Function],
+ "findAllByTestId": [Function],
+ "findAllByText": [Function],
+ "findAllByTitle": [Function],
+ "findByAltText": [Function],
+ "findByDisplayValue": [Function],
+ "findByLabelText": [Function],
+ "findByPlaceholderText": [Function],
+ "findByRole": [Function],
+ "findByTestId": [Function],
+ "findByText": [Function],
+ "findByTitle": [Function],
+ "getAllByAltText": [Function],
+ "getAllByDisplayValue": [Function],
+ "getAllByLabelText": [Function],
+ "getAllByPlaceholderText": [Function],
+ "getAllByRole": [Function],
+ "getAllByTestId": [Function],
+ "getAllByText": [Function],
+ "getAllByTitle": [Function],
+ "getByAltText": [Function],
+ "getByDisplayValue": [Function],
+ "getByLabelText": [Function],
+ "getByPlaceholderText": [Function],
+ "getByRole": [Function],
+ "getByTestId": [Function],
+ "getByText": [Function],
+ "getByTitle": [Function],
+ "queryAllByAltText": [Function],
+ "queryAllByDisplayValue": [Function],
+ "queryAllByLabelText": [Function],
+ "queryAllByPlaceholderText": [Function],
+ "queryAllByRole": [Function],
+ "queryAllByTestId": [Function],
+ "queryAllByText": [Function],
+ "queryAllByTitle": [Function],
+ "queryByAltText": [Function],
+ "queryByDisplayValue": [Function],
+ "queryByLabelText": [Function],
+ "queryByPlaceholderText": [Function],
+ "queryByRole": [Function],
+ "queryByTestId": [Function],
+ "queryByText": [Function],
+ "queryByTitle": [Function],
+ "rerender": [Function],
+ "unmount": [Function],
+}
+`;
+
+exports[`renderer/components/avatars/AvatarWithFallback.tsx renders the fallback icon when the image fails to load (isBroken = true) - human user 1`] = `
+
+
+
+ @gitify-app
+
+
+`;
+
+exports[`renderer/components/avatars/AvatarWithFallback.tsx renders the fallback icon when the image fails to load (isBroken = true) - non human user 1`] = `
+
+
+
+ @gitify-app
+
+
+`;
+
+exports[`renderer/components/avatars/AvatarWithFallback.tsx should render avatar - human user 1`] = `
+{
+ "asFragment": [Function],
+ "baseElement":
+
+
+
+
+ @gitify-app
+
+
+
+ ,
+ "container":
+
+
+
+ @gitify-app
+
+
+
,
+ "debug": [Function],
+ "findAllByAltText": [Function],
+ "findAllByDisplayValue": [Function],
+ "findAllByLabelText": [Function],
+ "findAllByPlaceholderText": [Function],
+ "findAllByRole": [Function],
+ "findAllByTestId": [Function],
+ "findAllByText": [Function],
+ "findAllByTitle": [Function],
+ "findByAltText": [Function],
+ "findByDisplayValue": [Function],
+ "findByLabelText": [Function],
+ "findByPlaceholderText": [Function],
+ "findByRole": [Function],
+ "findByTestId": [Function],
+ "findByText": [Function],
+ "findByTitle": [Function],
+ "getAllByAltText": [Function],
+ "getAllByDisplayValue": [Function],
+ "getAllByLabelText": [Function],
+ "getAllByPlaceholderText": [Function],
+ "getAllByRole": [Function],
+ "getAllByTestId": [Function],
+ "getAllByText": [Function],
+ "getAllByTitle": [Function],
+ "getByAltText": [Function],
+ "getByDisplayValue": [Function],
+ "getByLabelText": [Function],
+ "getByPlaceholderText": [Function],
+ "getByRole": [Function],
+ "getByTestId": [Function],
+ "getByText": [Function],
+ "getByTitle": [Function],
+ "queryAllByAltText": [Function],
+ "queryAllByDisplayValue": [Function],
+ "queryAllByLabelText": [Function],
+ "queryAllByPlaceholderText": [Function],
+ "queryAllByRole": [Function],
+ "queryAllByTestId": [Function],
+ "queryAllByText": [Function],
+ "queryAllByTitle": [Function],
+ "queryByAltText": [Function],
+ "queryByDisplayValue": [Function],
+ "queryByLabelText": [Function],
+ "queryByPlaceholderText": [Function],
+ "queryByRole": [Function],
+ "queryByTestId": [Function],
+ "queryByText": [Function],
+ "queryByTitle": [Function],
+ "rerender": [Function],
+ "unmount": [Function],
+}
+`;
+
+exports[`renderer/components/avatars/AvatarWithFallback.tsx should render avatar - non-human user 1`] = `
+{
+ "asFragment": [Function],
+ "baseElement":
+
+
+
+
+ @gitify-app
+
+
+
+ ,
+ "container":
+
+
+
+ @gitify-app
+
+
+
,
+ "debug": [Function],
+ "findAllByAltText": [Function],
+ "findAllByDisplayValue": [Function],
+ "findAllByLabelText": [Function],
+ "findAllByPlaceholderText": [Function],
+ "findAllByRole": [Function],
+ "findAllByTestId": [Function],
+ "findAllByText": [Function],
+ "findAllByTitle": [Function],
+ "findByAltText": [Function],
+ "findByDisplayValue": [Function],
+ "findByLabelText": [Function],
+ "findByPlaceholderText": [Function],
+ "findByRole": [Function],
+ "findByTestId": [Function],
+ "findByText": [Function],
+ "findByTitle": [Function],
+ "getAllByAltText": [Function],
+ "getAllByDisplayValue": [Function],
+ "getAllByLabelText": [Function],
+ "getAllByPlaceholderText": [Function],
+ "getAllByRole": [Function],
+ "getAllByTestId": [Function],
+ "getAllByText": [Function],
+ "getAllByTitle": [Function],
+ "getByAltText": [Function],
+ "getByDisplayValue": [Function],
+ "getByLabelText": [Function],
+ "getByPlaceholderText": [Function],
+ "getByRole": [Function],
+ "getByTestId": [Function],
+ "getByText": [Function],
+ "getByTitle": [Function],
+ "queryAllByAltText": [Function],
+ "queryAllByDisplayValue": [Function],
+ "queryAllByLabelText": [Function],
+ "queryAllByPlaceholderText": [Function],
+ "queryAllByRole": [Function],
+ "queryAllByTestId": [Function],
+ "queryAllByText": [Function],
+ "queryAllByTitle": [Function],
+ "queryByAltText": [Function],
+ "queryByDisplayValue": [Function],
+ "queryByLabelText": [Function],
+ "queryByPlaceholderText": [Function],
+ "queryByRole": [Function],
+ "queryByTestId": [Function],
+ "queryByText": [Function],
+ "queryByTitle": [Function],
+ "rerender": [Function],
+ "unmount": [Function],
+}
+`;
diff --git a/src/renderer/components/fields/RadioGroup.test.tsx b/src/renderer/components/fields/RadioGroup.test.tsx
index 67c3a8719..b55091935 100644
--- a/src/renderer/components/fields/RadioGroup.test.tsx
+++ b/src/renderer/components/fields/RadioGroup.test.tsx
@@ -27,7 +27,9 @@ describe('renderer/components/fields/RadioGroup.tsx', () => {
it('should check that NProgress is getting called in getDerivedStateFromProps (loading)', () => {
render( );
+
fireEvent.click(screen.getByLabelText('Value 1'));
+
expect(props.onChange).toHaveBeenCalledTimes(1);
});
});
diff --git a/src/renderer/components/notifications/AccountNotifications.tsx b/src/renderer/components/notifications/AccountNotifications.tsx
index c42452c66..db2f0fabc 100644
--- a/src/renderer/components/notifications/AccountNotifications.tsx
+++ b/src/renderer/components/notifications/AccountNotifications.tsx
@@ -1,7 +1,7 @@
import { type FC, type MouseEvent, useContext, useMemo, useState } from 'react';
import { GitPullRequestIcon, IssueOpenedIcon } from '@primer/octicons-react';
-import { Avatar, Button, IconButton, Stack, Text } from '@primer/react';
+import { Button, IconButton } from '@primer/react';
import { AppContext } from '../../context/App';
import { type Account, type GitifyError, Size } from '../../types';
@@ -15,6 +15,7 @@ import {
} from '../../utils/links';
import { AllRead } from '../AllRead';
import { Oops } from '../Oops';
+import { AvatarWithFallback } from '../avatars/AvatarWithFallback';
import { HoverGroup } from '../primitives/HoverGroup';
import { NotificationRow } from './NotificationRow';
import { RepositoryNotifications } from './RepositoryNotifications';
@@ -87,10 +88,12 @@ export const AccountNotifications: FC = (
}}
data-testid="account-profile"
>
-
-
- @{account.user.login}
-
+
diff --git a/src/renderer/components/notifications/NotificationFooter.tsx b/src/renderer/components/notifications/NotificationFooter.tsx
index 445799075..37655d41e 100644
--- a/src/renderer/components/notifications/NotificationFooter.tsx
+++ b/src/renderer/components/notifications/NotificationFooter.tsx
@@ -1,14 +1,13 @@
import type { FC, MouseEvent } from 'react';
-import { FeedPersonIcon, MarkGithubIcon } from '@primer/octicons-react';
-import { Avatar, RelativeTime, Stack, Text } from '@primer/react';
+import { Box, RelativeTime, Stack, Text } from '@primer/react';
-import { IconColor, Opacity, Size } from '../../types';
+import { Opacity, Size } from '../../types';
import type { Notification } from '../../typesGitHub';
import { cn } from '../../utils/cn';
-import { isNonHumanUser } from '../../utils/helpers';
import { openUserProfile } from '../../utils/links';
import { formatReason } from '../../utils/reason';
+import { AvatarWithFallback } from '../avatars/AvatarWithFallback';
import { MetricGroup } from '../metrics/MetricGroup';
interface INotificationFooter {
@@ -29,27 +28,32 @@ export const NotificationFooter: FC = ({
className={cn('text-xs', Opacity.MEDIUM)}
>
{notification.subject.user ? (
- ) => {
// Don't trigger onClick of parent element.
event.stopPropagation();
openUserProfile(notification.subject.user);
}}
data-testid="view-profile"
- />
+ >
+
+
) : (
- <>
- {notification.subject.type === 'RepositoryDependabotAlertsThread' ||
- notification.subject.type === 'RepositoryVulnerabilityAlert' ? (
-
- ) : (
-
- )}
- >
+
)}
diff --git a/src/renderer/components/notifications/NotificationHeader.tsx b/src/renderer/components/notifications/NotificationHeader.tsx
index f424ba541..7863429a7 100644
--- a/src/renderer/components/notifications/NotificationHeader.tsx
+++ b/src/renderer/components/notifications/NotificationHeader.tsx
@@ -1,12 +1,13 @@
import { type FC, type MouseEvent, useContext } from 'react';
-import { Avatar, Stack, Tooltip } from '@primer/react';
+import { Box, Stack, Tooltip } from '@primer/react';
import { AppContext } from '../../context/App';
import { GroupBy, Opacity, Size } from '../../types';
import type { Notification } from '../../typesGitHub';
import { cn } from '../../utils/cn';
import { openRepository } from '../../utils/links';
+import { AvatarWithFallback } from '../avatars/AvatarWithFallback';
interface INotificationHeader {
notification: Notification;
@@ -17,7 +18,6 @@ export const NotificationHeader: FC = ({
}: INotificationHeader) => {
const { settings } = useContext(AppContext);
- const repoAvatarUrl = notification.repository.owner.avatar_url;
const repoSlug = notification.repository.full_name;
const notificationNumber = notification.subject?.number
@@ -28,30 +28,36 @@ export const NotificationHeader: FC = ({
return (
groupByDate && (
-
- ) => {
- // Don't trigger onClick of parent element.
- event.stopPropagation();
- openRepository(notification.repository);
- }}
- data-testid="view-repository"
+
+
+ ) => {
+ // Don't trigger onClick of parent element.
+ event.stopPropagation();
+ openRepository(notification.repository);
+ }}
+ data-testid="view-repository"
+ >
+
+
+
+
-
-
- {repoSlug}
-
- {notificationNumber}
-
-
-
-
+ {notificationNumber}
+
+
)
);
};
diff --git a/src/renderer/components/notifications/RepositoryNotifications.tsx b/src/renderer/components/notifications/RepositoryNotifications.tsx
index d0e997c1c..1714f6a52 100644
--- a/src/renderer/components/notifications/RepositoryNotifications.tsx
+++ b/src/renderer/components/notifications/RepositoryNotifications.tsx
@@ -1,5 +1,5 @@
import { CheckIcon, ReadIcon } from '@primer/octicons-react';
-import { Avatar, Button, IconButton, Stack, Text } from '@primer/react';
+import { Button, IconButton } from '@primer/react';
import { type FC, type MouseEvent, useContext, useState } from 'react';
import { AppContext } from '../../context/App';
@@ -7,8 +7,9 @@ import { Opacity, Size } from '../../types';
import type { Notification } from '../../typesGitHub';
import { cn } from '../../utils/cn';
import { isMarkAsDoneFeatureSupported } from '../../utils/features';
-import { getChevronDetails, isNonHumanUser } from '../../utils/helpers';
+import { getChevronDetails } from '../../utils/helpers';
import { openRepository } from '../../utils/links';
+import { AvatarWithFallback } from '../avatars/AvatarWithFallback';
import { HoverGroup } from '../primitives/HoverGroup';
import { NotificationRow } from './NotificationRow';
@@ -65,16 +66,13 @@ export const RepositoryNotifications: FC = ({
openRepository(repoNotifications[0].repository);
}}
>
-
-
- {repoName}
-
+
diff --git a/src/renderer/components/notifications/__snapshots__/AccountNotifications.test.tsx.snap b/src/renderer/components/notifications/__snapshots__/AccountNotifications.test.tsx.snap
index 59dd6950b..38eaea97a 100644
--- a/src/renderer/components/notifications/__snapshots__/AccountNotifications.test.tsx.snap
+++ b/src/renderer/components/notifications/__snapshots__/AccountNotifications.test.tsx.snap
@@ -33,10 +33,11 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should rende
data-gap="condensed"
data-justify="start"
data-padding="none"
+ data-testid="avatar"
data-wrap="nowrap"
>
- @
- octocat
+ @octocat
@@ -284,10 +284,11 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should rende
data-gap="condensed"
data-justify="start"
data-padding="none"
+ data-testid="avatar"
data-wrap="nowrap"
>
- @
- octocat
+ @octocat
@@ -592,10 +592,11 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should rende
data-gap="condensed"
data-justify="start"
data-padding="none"
+ data-testid="avatar"
data-wrap="nowrap"
>
- @
- octocat
+ @octocat
@@ -800,45 +800,57 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should rende
-
+
+
+
-
+ >
+
+
+
+
-
+
+
+
- @
- octocat
+ @octocat
@@ -1607,45 +1657,57 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should rende
-
+
+
+
-
+ >
+
+
+
+
-
+
+
+
- @
- octocat
+ @octocat
@@ -2468,10 +2568,11 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should rende
data-gap="condensed"
data-justify="start"
data-padding="none"
+ data-testid="avatar"
data-wrap="nowrap"
>
- @
- octocat
+ @octocat
@@ -2730,10 +2830,11 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should rende
data-gap="condensed"
data-justify="start"
data-padding="none"
+ data-testid="avatar"
data-wrap="nowrap"
>
- @
- octocat
+ @octocat
@@ -2976,10 +3076,11 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should rende
data-gap="condensed"
data-justify="start"
data-padding="none"
+ data-testid="avatar"
data-wrap="nowrap"
>
- @
- octocat
+ @octocat
@@ -3279,10 +3379,11 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should toggl
data-gap="condensed"
data-justify="start"
data-padding="none"
+ data-testid="avatar"
data-wrap="nowrap"
>
- @
- octocat
+ @octocat
@@ -3480,10 +3580,11 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should toggl
data-gap="condensed"
data-justify="start"
data-padding="none"
+ data-testid="avatar"
data-wrap="nowrap"
>
- @
- octocat
+ @octocat
@@ -3685,10 +3785,11 @@ exports[`renderer/components/notifications/AccountNotifications.tsx should toggl
data-gap="condensed"
data-justify="start"
data-padding="none"
+ data-testid="avatar"
data-wrap="nowrap"
>
- @
- octocat
+ @octocat
diff --git a/src/renderer/components/notifications/__snapshots__/NotificationFooter.test.tsx.snap b/src/renderer/components/notifications/__snapshots__/NotificationFooter.test.tsx.snap
index 022d35715..6f826c09f 100644
--- a/src/renderer/components/notifications/__snapshots__/NotificationFooter.test.tsx.snap
+++ b/src/renderer/components/notifications/__snapshots__/NotificationFooter.test.tsx.snap
@@ -14,18 +14,33 @@ exports[`renderer/components/notifications/NotificationFooter.tsx security alert
data-padding="none"
data-wrap="wrap"
>
-
+ >
+
+
+
+
-
+ >
+
+
+
+
-
+ >
+
+
+
+
-
+ >
+
+
+
+
-
+ >
+
+
+
+
-
+ >
+
+
+
+
-
+ >
+
+
+
+
-
+ >
+
+
+
+
+
+
+
+
+
+
+ gitify-app/notifications-test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gitify-app/notifications-test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gitify-app/notifications-test
+
+
+
+
+
+
+
+