Skip to content
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
width: 100%;
-webkit-box-orient: vertical;
color: variables.$grey-4;
hyphens: auto;
-webkit-line-clamp: 2;
word-wrap: break-word;

Expand Down
36 changes: 30 additions & 6 deletions src/lib/helpers/createNotificationData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { page } from '$app/stores';
import { getDiscussionUrl } from '$lib/helpers/searchNotificationHelper';
import {
ClosedIssueIcon,
CommitIcon,
Expand Down Expand Up @@ -43,10 +44,11 @@ type PullRequestEvent = {
type FetchOptions = Parameters<typeof fetchGithub>[1];

export async function createNotificationData(
{ id, repository, subject, unread: isUnread, updated_at, reason }: GithubNotification,
githubNotification: GithubNotification,
savedNotifications: SavedNotifications,
firstTime: boolean
): Promise<NotificationData | null> {
const { id, repository, subject, unread: isUnread, updated_at, reason } = githubNotification;
const previous = Array.isArray(savedNotifications)
? savedNotifications.find((n) => n.id === id)
: undefined;
Expand Down Expand Up @@ -267,14 +269,32 @@ export async function createNotificationData(
break;
}

case 'Discussion':
case 'Discussion': {
const data = await getDiscussionUrl(githubNotification).then(({ url, latestCommentEdge }) => {
if (!latestCommentEdge) {
return {
description: 'New activity on discussion'
};
}
url += '#discussioncomment-' + latestCommentEdge.node.databaseId;
const author = latestCommentEdge.node.author;
return {
author: {
login: author.login,
avatar: author.avatarUrl,
bot: author.__typename === 'Bot'
},
description: commentBodyToDescription(latestCommentEdge.node.bodyText),
url
};
});
value = {
...common,
description: 'New activity on discussion',
...data,
icon: DiscussionIcon
};
break;

}
case 'CheckSuite': {
const splited = subject.title.split(' ');
const workflowName = splited[0];
Expand Down Expand Up @@ -349,6 +369,10 @@ export async function createNotificationData(
};
}

const commentBodyToDescription = (body: string) => {
return `*commented*: _${body.slice(0, 100)}${body.length > 100 ? '...' : ''}_`;
};

async function getLatestComment(
url: string,
fetchOptions: FetchOptions
Expand All @@ -361,8 +385,8 @@ async function getLatestComment(
avatar: comment.user.avatar_url,
bot: comment.user.type === 'Bot'
};
const body = removeMarkdownSymbols(comment.body).slice(0, 100);
const description = `*commented*: _${body}${body.length < 100 ? '...' : ''}_`;
const body = removeMarkdownSymbols(comment.body);
const description = commentBodyToDescription(body);
return { author, description, time: comment.created_at, url: comment.html_url };
}

Expand Down
5 changes: 2 additions & 3 deletions src/lib/helpers/fetchGithub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ export async function fetchGithub<T = void>(url: string, options?: Options): Pro
cache: options?.noCache ? 'no-store' : undefined
});

if (options?.method) return undefined as T;
if (options?.method === 'PATCH') return undefined as T;

if (response.ok) {
const data = await response.json();
return data;
return await response.json();
}

throw new Error(`${response.status}`);
Expand Down
142 changes: 142 additions & 0 deletions src/lib/helpers/searchNotificationHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// source code from https://github.com/gitify-app/gitify/pull/538
import { fetchGithub } from '$lib/helpers/fetchGithub';
import type { GithubNotification } from '$lib/types';

export type ViewerSubscription = 'IGNORED' | 'SUBSCRIBED' | 'UNSUBSCRIBED';

export interface GraphQLSearch {
data: {
search: {
edges: DiscussionEdge[];
};
};
}

export interface DiscussionEdge {
node: {
viewerSubscription: ViewerSubscription;
title: string;
url: string;
comments: {
edges: DiscussionCommentEdge[];
};
};
}

// https://docs.github.com/en/graphql/reference/interfaces#actor
export interface Actor {
login: string;
avatarUrl: string;
__typename: 'Bot' | 'EnterpriseUserAccount' | 'Mannequin' | 'Organization' | 'User';
}

export interface DiscussionCommentEdge {
node: {
databaseId: string | number;
createdAt: string;
author: Actor;
bodyText: string;
replies: {
edges: DiscussionSubCommentEdge[];
};
};
}

export interface DiscussionSubCommentEdge {
node: {
databaseId: string | number;
createdAt: string;
author: Actor;
bodyText: string;
};
}

const addHours = (date: string, hours: number) =>
new Date(new Date(date).getTime() + hours * 36e5).toISOString();

const queryString = (repo: string, title: string, lastUpdated: string) =>
`${title} in:title repo:${repo} updated:>${addHours(lastUpdated, -2)}`;

export const getLatestDiscussionCommentEdge = (comments: DiscussionCommentEdge[]) =>
comments
.flatMap((comment) => comment.node.replies.edges)
.concat([comments.at(-1) || ({} as DiscussionCommentEdge)])
.reduce((a, b) => (a.node.createdAt > b.node.createdAt ? a : b));

export async function getDiscussionUrl(notification: GithubNotification): Promise<{
url: string;
latestCommentEdge: DiscussionSubCommentEdge | undefined;
}> {
const response: GraphQLSearch = await fetchGithub('graphql', {
method: 'POST',
body: {
query: `{
search(query:"${queryString(
notification.repository.full_name,
notification.subject.title,
notification.updated_at
)}"
type: DISCUSSION
first: 10
) {
edges {
node {
... on Discussion {
viewerSubscription
title
url
comments(last: 100) {
edges {
node {
author {
login
avatarUrl
__typename
}
bodyText
databaseId
createdAt
replies(last: 1) {
edges {
node {
databaseId
createdAt
author {
login
avatarUrl
__typename
}
bodyText
}
}
}
}
}
}
}
}
}
}
}`
}
});

let edges =
response?.data?.search?.edges?.filter(
(edge) => edge.node.title === notification.subject.title
) || [];
if (edges.length > 1)
edges = edges.filter((edge) => edge.node.viewerSubscription === 'SUBSCRIBED');

const comments = edges[0]?.node.comments.edges;

let latestCommentEdge: DiscussionSubCommentEdge | undefined;
if (comments?.length) {
latestCommentEdge = getLatestDiscussionCommentEdge(comments);
}

return {
url: edges[0]?.node.url,
latestCommentEdge
};
}