From 09020ae25bb19bc858622abe0d3c4c1d8586e296 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 5 Mar 2022 16:07:36 +0200 Subject: [PATCH 1/7] fix discussions-url --- src/components/NotificationRow.tsx | 12 ++++++++- src/utils/helpers.ts | 41 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx index eb23e75d5..53f59de80 100644 --- a/src/components/NotificationRow.tsx +++ b/src/components/NotificationRow.tsx @@ -5,7 +5,7 @@ import { formatDistanceToNow, parseISO } from 'date-fns'; import { CheckIcon, MuteIcon } from '@primer/octicons-react'; import { formatReason, getNotificationTypeIcon } from '../utils/github-api'; -import { generateGitHubWebUrl } from '../utils/helpers'; +import { generateGitHubWebUrl, getDiscussionUrl } from '../utils/helpers'; import { Notification } from '../typesGithub'; import { AppContext } from '../context/App'; @@ -38,6 +38,16 @@ export const NotificationRow: React.FC = ({ accounts.user?.id ); shell.openExternal(url); + } else if (notification.subject.type === 'Discussion') { + getDiscussionUrl(notification, accounts.token).then(url => + shell.openExternal( + generateGitHubWebUrl( + url || `${notification.repository.url}/discussions`, + notification.id, + accounts.user?.id + ) + ) + ); } }, [notification]); diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index dbeb33a48..ba506b2a7 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,4 +1,6 @@ import { EnterpriseAccount } from '../types'; +import { Notification } from '../typesGithub'; +import { apiRequestAuth } from '../utils/api-requests'; import { Constants } from './constants'; @@ -59,3 +61,42 @@ export function generateGitHubWebUrl( return newUrl; } + +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 async function getDiscussionUrl( + notification: Notification, + token: string +): Promise { + const response = await apiRequestAuth(`https://api.github.com/graphql`, 'POST', token, { + 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 + } + } + } + } + }`, + }); + let edges = response?.data?.data?.search?.edges?.filter( + edge => edge.node.title === notification.subject.title + ) || []; + if (edges.length > 1) + edges = edges.filter( + edge => edge.node.viewerSubscription === 'SUBSCRIBED' + ); + return edges[0]?.node.url; +} From fce57b6074f67e07dbc3c291ff25ab4e9bfb8055 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 5 Mar 2022 17:18:41 +0200 Subject: [PATCH 2/7] lint --- src/utils/helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index ba506b2a7..fca771ac5 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -66,7 +66,7 @@ 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)}`; + `${title} in:title repo:${repo} updated:>${addHours(lastUpdated, -2)}`; export async function getDiscussionUrl( notification: Notification, @@ -89,7 +89,7 @@ export async function getDiscussionUrl( } } } - }`, + }` }); let edges = response?.data?.data?.search?.edges?.filter( edge => edge.node.title === notification.subject.title From b4eecd207e667cb0ca2608ab036c80b54faffe23 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 5 Mar 2022 18:05:36 +0200 Subject: [PATCH 3/7] fix discussions-url in native notification --- src/hooks/useNotifications.ts | 2 +- src/utils/notifications.test.ts | 13 +++++++------ src/utils/notifications.ts | 29 ++++++++++++++++++++--------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/hooks/useNotifications.ts b/src/hooks/useNotifications.ts index 88e670cb2..e44068392 100644 --- a/src/hooks/useNotifications.ts +++ b/src/hooks/useNotifications.ts @@ -100,7 +100,7 @@ export const useNotifications = (): NotificationsState => { notifications, data, settings, - accounts.user + accounts ); setNotifications(data); setIsFetching(false); diff --git a/src/utils/notifications.test.ts b/src/utils/notifications.test.ts index 86bea2f3a..87def9d5a 100644 --- a/src/utils/notifications.test.ts +++ b/src/utils/notifications.test.ts @@ -7,6 +7,7 @@ import { mockedSingleAccountNotifications, mockedUser, } from '../__mocks__/mockedData'; +import { mockAccounts } from '../__mocks__/mock-state'; import * as comms from './comms'; import * as notificationsHelpers from './notifications'; import { SettingsState } from '../types'; @@ -27,7 +28,7 @@ describe('utils/notifications.ts', () => { [], mockedAccountNotifications, settings, - mockedUser + mockAccounts ); expect(notificationsHelpers.raiseNativeNotification).toHaveBeenCalledTimes( @@ -52,7 +53,7 @@ describe('utils/notifications.ts', () => { [], mockedAccountNotifications, settings, - mockedUser + mockAccounts ); expect(notificationsHelpers.raiseNativeNotification).not.toHaveBeenCalled(); @@ -73,7 +74,7 @@ describe('utils/notifications.ts', () => { mockedSingleAccountNotifications, mockedSingleAccountNotifications, settings, - mockedUser + mockAccounts ); expect(notificationsHelpers.raiseNativeNotification).not.toHaveBeenCalled(); @@ -94,7 +95,7 @@ describe('utils/notifications.ts', () => { [], [], settings, - mockedUser + mockAccounts ); expect(notificationsHelpers.raiseNativeNotification).not.toHaveBeenCalled(); @@ -106,7 +107,7 @@ describe('utils/notifications.ts', () => { const nativeNotification: Notification = notificationsHelpers.raiseNativeNotification( [mockedGithubNotifications[0]], - mockedUser.id + mockAccounts ); nativeNotification.onclick(null); @@ -125,7 +126,7 @@ describe('utils/notifications.ts', () => { const nativeNotification = notificationsHelpers.raiseNativeNotification( mockedGithubNotifications, - mockedUser.id + mockAccounts ); nativeNotification.onclick(null); diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 5e9a4e12c..00ad88be8 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -1,10 +1,10 @@ -const { remote } = require('electron'); +const { remote, shell } = require('electron'); -import { generateGitHubWebUrl } from './helpers'; +import { generateGitHubWebUrl, getDiscussionUrl } from './helpers'; import { reOpenWindow, openExternalLink, updateTrayIcon } from './comms'; -import { Notification, User } from '../typesGithub'; +import { Notification } from '../typesGithub'; -import { AccountNotifications, SettingsState } from '../types'; +import { AccountNotifications, SettingsState, AuthState } from '../types'; export const setTrayIconColor = (notifications: AccountNotifications[]) => { const allNotificationsCount = notifications.reduce( @@ -19,7 +19,7 @@ export const triggerNativeNotifications = ( previousNotifications: AccountNotifications[], newNotifications: AccountNotifications[], settings: SettingsState, - user: User + accounts: AuthState ) => { const diffNotifications = newNotifications .map((account) => { @@ -55,13 +55,13 @@ export const triggerNativeNotifications = ( } if (settings.showNotifications) { - raiseNativeNotification(diffNotifications, user?.id); + raiseNativeNotification(diffNotifications, accounts); } }; export const raiseNativeNotification = ( notifications: Notification[], - userId?: number + accounts: AuthState ) => { let title: string; let body: string; @@ -87,11 +87,22 @@ export const raiseNativeNotification = ( const appWindow = remote.getCurrentWindow(); appWindow.hide(); + const { subject, id, repository } = notifications[0]; + // Some Notification types from GitHub are missing urls in their subjects. if (notificationUrl) { - const { subject, id } = notifications[0]; - const url = generateGitHubWebUrl(subject.url, id, userId); + const url = generateGitHubWebUrl(subject.url, id, accounts.user?.id); openExternalLink(url); + } else if (notifications[0].subject.type === 'Discussion') { + getDiscussionUrl(notifications[0], accounts.token).then(url => + shell.openExternal( + generateGitHubWebUrl( + url || `${repository.url}/discussions`, + id, + accounts.user?.id + ) + ) + ); } } else { reOpenWindow(); From e42fd54bea782e37bb45a938fef24d791ebb616d Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Sat, 5 Mar 2022 18:56:11 +0200 Subject: [PATCH 4/7] lint --- src/components/NotificationRow.tsx | 14 +++++++++----- src/utils/notifications.ts | 6 +++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx index 53f59de80..aa39f1a2a 100644 --- a/src/components/NotificationRow.tsx +++ b/src/components/NotificationRow.tsx @@ -1,4 +1,4 @@ -const { shell } = require('electron'); +import { openExternalLink } from '../utils/comms'; import React, { useCallback, useContext } from 'react'; import { formatDistanceToNow, parseISO } from 'date-fns'; @@ -18,8 +18,12 @@ export const NotificationRow: React.FC = ({ notification, hostname, }) => { - const { settings, accounts } = useContext(AppContext); - const { markNotification, unsubscribeNotification } = useContext(AppContext); + const { + settings, + accounts, + markNotification, + unsubscribeNotification, + } = useContext(AppContext); const pressTitle = useCallback(() => { openBrowser(); @@ -37,10 +41,10 @@ export const NotificationRow: React.FC = ({ notification.id, accounts.user?.id ); - shell.openExternal(url); + openExternalLink(url); } else if (notification.subject.type === 'Discussion') { getDiscussionUrl(notification, accounts.token).then(url => - shell.openExternal( + openExternalLink( generateGitHubWebUrl( url || `${notification.repository.url}/discussions`, notification.id, diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 00ad88be8..2d9850130 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -1,4 +1,4 @@ -const { remote, shell } = require('electron'); +const { remote } = require('electron'); import { generateGitHubWebUrl, getDiscussionUrl } from './helpers'; import { reOpenWindow, openExternalLink, updateTrayIcon } from './comms'; @@ -93,9 +93,9 @@ export const raiseNativeNotification = ( if (notificationUrl) { const url = generateGitHubWebUrl(subject.url, id, accounts.user?.id); openExternalLink(url); - } else if (notifications[0].subject.type === 'Discussion') { + } else if (subject.type === 'Discussion') { getDiscussionUrl(notifications[0], accounts.token).then(url => - shell.openExternal( + openExternalLink( generateGitHubWebUrl( url || `${repository.url}/discussions`, id, From 8c93f916a35d9bb05e7f91c9fab1b1cf8543250a Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Tue, 8 Mar 2022 18:13:57 +0200 Subject: [PATCH 5/7] add latest comment id to url --- .gitignore | 1 + package.json | 2 +- src/__mocks__/electron.js | 28 +- src/__mocks__/mockedData.ts | 115 +++- src/components/NotificationRow.tsx | 26 +- .../LoginWithToken.test.tsx.snap | 2 +- src/typesGithub.ts | 43 ++ src/utils/auth.test.ts | 16 +- src/utils/constants.ts | 2 +- src/utils/helpers.test.ts | 149 +++-- src/utils/helpers.ts | 81 ++- src/utils/notifications.test.ts | 5 +- src/utils/notifications.ts | 28 +- tsconfig.json | 2 +- yarn.lock | 574 +++++++++++++----- 15 files changed, 759 insertions(+), 315 deletions(-) diff --git a/.gitignore b/.gitignore index 12fa30526..5dcbb3981 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,4 @@ temp/ dist/ build/ +.vscode/settings.json diff --git a/package.json b/package.json index 0893d8b5c..c4a14b149 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "react-transition-group": "^4.4.1", "tailwindcss": "^2.0.2", "ts-loader": "^8.0.12", - "typescript": "^4.1.3" + "typescript": "^4.6.2" }, "devDependencies": { "@testing-library/react": "^11.2.2", diff --git a/src/__mocks__/electron.js b/src/__mocks__/electron.js index 56a5502e9..97bf91c0f 100644 --- a/src/__mocks__/electron.js +++ b/src/__mocks__/electron.js @@ -27,18 +27,26 @@ window.localStorage = { window.alert = jest.fn(); -const browserWindow = { - loadURL: jest.fn(), - webContents: { +let instance; + +class BrowserWindow { + constructor() { + if(!instance){ + instance = this; + } + return instance; + } + loadURL = jest.fn(); + webContents = { on: () => {}, session: { clearStorageData: jest.fn(), }, - }, - on: () => {}, - close: jest.fn(), - hide: jest.fn(), - destroy: jest.fn(), + }; + on(){}; + close = jest.fn(); + hide = jest.fn(); + destroy = jest.fn(); }; const dialog = { @@ -47,7 +55,7 @@ const dialog = { module.exports = { remote: { - BrowserWindow: () => browserWindow, + BrowserWindow: BrowserWindow, dialog: dialog, process: { platform: 'darwin', @@ -57,7 +65,7 @@ module.exports = { getLoginItemSettings: jest.fn(), setLoginItemSettings: () => {}, }, - getCurrentWindow: jest.fn(() => browserWindow), + getCurrentWindow: jest.fn(() => instance || new BrowserWindow), }, ipcRenderer: { send: jest.fn(), diff --git a/src/__mocks__/mockedData.ts b/src/__mocks__/mockedData.ts index c18d0184a..08b94e0b5 100644 --- a/src/__mocks__/mockedData.ts +++ b/src/__mocks__/mockedData.ts @@ -1,5 +1,5 @@ import { AccountNotifications, EnterpriseAccount } from '../types'; -import { Notification, Repository, User } from '../typesGithub'; +import { Notification, Repository, User, GraphQLSearch } from '../typesGithub'; export const mockedEnterpriseAccounts: EnterpriseAccount[] = [ { @@ -274,3 +274,116 @@ export const mockedSingleAccountNotifications: AccountNotifications[] = [ notifications: [mockedSingleNotification], }, ]; + +export const mockedGraphQLResponse: GraphQLSearch = { + "data": { + "data": { + "search": { + "edges": [ + { + "node": { + "viewerSubscription": "SUBSCRIBED", + "title": "1.16.0", + "url": "https://github.com/manosim/notifications-test/discussions/612", + "comments": { + "edges": [ + { + "node": { + "databaseId": 2215656, + "createdAt": "2022-02-20T18:33:39Z", + "replies": { + "edges": [] + } + } + }, + { + "node": { + "databaseId": 2217789, + "createdAt": "2022-02-21T03:30:42Z", + "replies": { + "edges": [] + } + } + }, + { + "node": { + "databaseId": 2223243, + "createdAt": "2022-02-21T18:26:27Z", + "replies": { + "edges": [ + { + "node": { + "databaseId": 2232922, + "createdAt": "2022-02-23T00:57:58Z" + } + } + ] + } + } + }, + { + "node": { + "databaseId": 2232921, + "createdAt": "2022-02-23T00:57:49Z", + "replies": { + "edges": [] + } + } + }, + { + "node": { + "databaseId": 2258799, + "createdAt": "2022-02-27T01:22:20Z", + "replies": { + "edges": [ + { + "node": { + "databaseId": 2300902, + "createdAt": "2022-03-05T17:43:52Z" + } + } + ] + } + } + }, + { + "node": { + "databaseId": 2297637, + "createdAt": "2022-03-04T20:39:44Z", + "replies": { + "edges": [ + { + "node": { + "databaseId": 2300893, + "createdAt": "2022-03-05T17:41:04Z" + } + } + ] + } + } + }, + { + "node": { + "databaseId": 2299763, + "createdAt": "2022-03-05T11:05:42Z", + "replies": { + "edges": [ + { + "node": { + "databaseId": 2300895, + "createdAt": "2022-03-05T17:41:44Z" + } + } + ] + } + } + } + ] + } + } + } + ] + } + } + } +} diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx index aa39f1a2a..9baff8237 100644 --- a/src/components/NotificationRow.tsx +++ b/src/components/NotificationRow.tsx @@ -1,11 +1,9 @@ -import { openExternalLink } from '../utils/comms'; - import React, { useCallback, useContext } from 'react'; import { formatDistanceToNow, parseISO } from 'date-fns'; import { CheckIcon, MuteIcon } from '@primer/octicons-react'; import { formatReason, getNotificationTypeIcon } from '../utils/github-api'; -import { generateGitHubWebUrl, getDiscussionUrl } from '../utils/helpers'; +import { openInBrowser } from '../utils/helpers'; import { Notification } from '../typesGithub'; import { AppContext } from '../context/App'; @@ -33,27 +31,7 @@ export const NotificationRow: React.FC = ({ } }, [settings]); - const openBrowser = useCallback(() => { - // Some Notification types from GitHub are missing urls in their subjects. - if (notification.subject.url) { - const url = generateGitHubWebUrl( - notification.subject.url, - notification.id, - accounts.user?.id - ); - openExternalLink(url); - } else if (notification.subject.type === 'Discussion') { - getDiscussionUrl(notification, accounts.token).then(url => - openExternalLink( - generateGitHubWebUrl( - url || `${notification.repository.url}/discussions`, - notification.id, - accounts.user?.id - ) - ) - ); - } - }, [notification]); + const openBrowser = useCallback(() => openInBrowser(notification, accounts), [notification]); const unsubscribe = (event: React.MouseEvent) => { // Don't trigger onClick of parent element. diff --git a/src/routes/__snapshots__/LoginWithToken.test.tsx.snap b/src/routes/__snapshots__/LoginWithToken.test.tsx.snap index 518cd5d65..f591e733d 100644 --- a/src/routes/__snapshots__/LoginWithToken.test.tsx.snap +++ b/src/routes/__snapshots__/LoginWithToken.test.tsx.snap @@ -84,7 +84,7 @@ exports[`routes/LoginWithToken.js renders correctly 1`] = ` - read:user, notifications + read:user, notifications, repo scopes. diff --git a/src/typesGithub.ts b/src/typesGithub.ts index 1ff4e7810..2128fbce5 100644 --- a/src/typesGithub.ts +++ b/src/typesGithub.ts @@ -21,6 +21,11 @@ export type SubjectType = | 'Release' | 'RepositoryVulnerabilityAlert'; +export type ViewerSubscription = + | 'IGNORED' + | 'SUBSCRIBED' + | 'UNSUBSCRIBED' + export interface Notification { id: string; unread: boolean; @@ -115,3 +120,41 @@ export interface Subject { latest_comment_url?: string; type: SubjectType; } + +export interface GraphQLSearch { + data: { + data: { + search: { + edges: DiscussionEdge[] + } + } + } +} + +export interface DiscussionEdge { + node: { + viewerSubscription: ViewerSubscription; + title: string; + url: string; + comments: { + edges: DiscussionCommentEdge[] + } + } +} + +export interface DiscussionCommentEdge { + node: { + databaseId: string|number; + createdAt: string; + replies: { + edges: DiscussionSubcommentEdge[] + } + } +} + +export interface DiscussionSubcommentEdge { + node: { + databaseId: string|number; + createdAt: string; + } +} diff --git a/src/utils/auth.test.ts b/src/utils/auth.test.ts index 9085e164a..d31708d66 100644 --- a/src/utils/auth.test.ts +++ b/src/utils/auth.test.ts @@ -1,7 +1,7 @@ import { AxiosPromise, AxiosResponse } from 'axios'; -const { remote } = require('electron'); -const BrowserWindow = remote.BrowserWindow; +import { remote } from 'electron'; +const browserWindow = new remote.BrowserWindow import * as auth from './auth'; import * as apiRequests from './api-requests'; @@ -9,14 +9,14 @@ import { AuthState } from '../types'; describe('utils/auth.tsx', () => { describe('authGitHub', () => { - const loadURLMock = jest.spyOn(new BrowserWindow(), 'loadURL'); + const loadURLMock = jest.spyOn(browserWindow, 'loadURL'); beforeEach(() => { loadURLMock.mockReset(); }); it('should call authGithub - success', async () => { - spyOn(new BrowserWindow().webContents, 'on').and.callFake( + spyOn(browserWindow.webContents, 'on').and.callFake( (event, callback) => { if (event === 'will-redirect') { const event = new Event('will-redirect'); @@ -30,19 +30,19 @@ describe('utils/auth.tsx', () => { expect(res.authCode).toBe('123-456'); expect( - new BrowserWindow().webContents.session.clearStorageData + browserWindow.webContents.session.clearStorageData ).toHaveBeenCalledTimes(1); expect(loadURLMock).toHaveBeenCalledTimes(1); expect(loadURLMock).toHaveBeenCalledWith( - 'https://github.com/login/oauth/authorize?client_id=FAKE_CLIENT_ID_123&scope=read:user,notifications' + 'https://github.com/login/oauth/authorize?client_id=FAKE_CLIENT_ID_123&scope=read:user,notifications,repo' ); - expect(new BrowserWindow().destroy).toHaveBeenCalledTimes(1); + expect(browserWindow.destroy).toHaveBeenCalledTimes(1); }); it('should call authGithub - failure', async () => { - spyOn(new BrowserWindow().webContents, 'on').and.callFake( + spyOn(browserWindow.webContents, 'on').and.callFake( (event, callback) => { if (event === 'will-redirect') { const event = new Event('will-redirect'); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 26a2eddb9..1df843ed6 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,6 +1,6 @@ export const Constants = { // GitHub OAuth - AUTH_SCOPE: ['read:user', 'notifications'], + AUTH_SCOPE: ['read:user', 'notifications', 'repo'], DEFAULT_AUTH_OPTIONS: { hostname: 'github.com', diff --git a/src/utils/helpers.test.ts b/src/utils/helpers.test.ts index 079373b0f..1ea265874 100644 --- a/src/utils/helpers.test.ts +++ b/src/utils/helpers.test.ts @@ -2,8 +2,21 @@ import { generateGitHubWebUrl, generateGitHubAPIUrl, generateNotificationReferrerId, + getCommentId, + getLatestDiscussionCommentId } from './helpers'; -import { mockedSingleNotification, mockedUser } from '../__mocks__/mockedData'; +import { mockedSingleNotification, mockedUser, mockedGraphQLResponse } from '../__mocks__/mockedData'; + +const URL = { + normal: { + api: 'https://api.github.com/repos/myuser/notifications-test', + default: 'https://github.com/myuser/notifications-test' + }, + enterprise: { + api: 'https://github.gitify.io/api/v3/repos/myorg/notifications-test', + default: 'https://github.gitify.io/myorg/notifications-test' + } +} describe('utils/helpers.ts', () => { describe('generateNotificationReferrerId', () => { @@ -27,89 +40,69 @@ describe('utils/helpers.ts', () => { ); }); - it('should generate the GitHub url - non enterprise - (issue)', () => { - const apiUrl = - 'https://api.github.com/repos/ekonstantinidis/notifications-test/issues/3'; - const notif = { ...mockedSingleNotification, subject: { url: apiUrl } }; - const newUrl = generateGitHubWebUrl( - notif.subject.url, - notif.id, - mockedUser.id - ); - expect(newUrl).toBe( - `https://github.com/ekonstantinidis/notifications-test/issues/3?${notificationReferrerId}` - ); - }); + it('should generate the GitHub url - non enterprise - (issue)', () => testGenerateUrl( + `${URL.normal.api}/issues/3`, + `${URL.normal.default}/issues/3?${notificationReferrerId}`) + ); - it('should generate the GitHub url - non enterprise - (pull request)', () => { - const apiUrl = - 'https://api.github.com/repos/ekonstantinidis/notifications-test/pulls/123'; - const notif = { ...mockedSingleNotification, subject: { url: apiUrl } }; - const newUrl = generateGitHubWebUrl( - notif.subject.url, - notif.id, - mockedUser.id - ); - expect(newUrl).toBe( - `https://github.com/ekonstantinidis/notifications-test/pull/123?${notificationReferrerId}` - ); - }); + it('should generate the GitHub url - non enterprise - (pull request)', () => testGenerateUrl( + `${URL.normal.api}/pulls/123`, + `${URL.normal.default}/pull/123?${notificationReferrerId}`) + ); - it('should generate the GitHub url - non enterprise - (release)', () => { - const apiUrl = - 'https://api.github.com/repos/myorg/notifications-test/releases/3988077'; - const notif = { ...mockedSingleNotification, subject: { url: apiUrl } }; - const newUrl = generateGitHubWebUrl( - notif.subject.url, - notif.id, - mockedUser.id - ); - expect(newUrl).toBe( - `https://github.com/myorg/notifications-test/releases?${notificationReferrerId}` - ); - }); + it('should generate the GitHub url - non enterprise - (release)', () => testGenerateUrl( + `${URL.normal.api}/releases/3988077`, + `${URL.normal.default}/releases?${notificationReferrerId}`) + ); - it('should generate the GitHub url - enterprise - (issue)', () => { - const apiUrl = - 'https://github.gitify.io/api/v3/repos/myorg/notifications-test/issues/123'; - const notif = { ...mockedSingleNotification, subject: { url: apiUrl } }; - const newUrl = generateGitHubWebUrl( - notif.subject.url, - notif.id, - mockedUser.id - ); - expect(newUrl).toBe( - `https://github.gitify.io/myorg/notifications-test/issues/123?${notificationReferrerId}` - ); - }); + it('should generate the GitHub url - non enterprise - (discussion)', () => testGenerateUrl( + `${URL.normal.api}/discussions/630`, + `${URL.normal.default}/discussions/630?${notificationReferrerId}`) + ); - it('should generate the GitHub url - enterprise - (pull request)', () => { - const apiUrl = - 'https://github.gitify.io/api/v3/repos/myorg/notifications-test/pulls/3'; - const notif = { ...mockedSingleNotification, subject: { url: apiUrl } }; - const newUrl = generateGitHubWebUrl( - notif.subject.url, - notif.id, - mockedUser.id - ); - expect(newUrl).toBe( - `https://github.gitify.io/myorg/notifications-test/pull/3?${notificationReferrerId}` - ); - }); + it('should generate the GitHub url - enterprise - (issue)', () => testGenerateUrl( + `${URL.enterprise.api}/issues/123`, + `${URL.enterprise.default}/issues/123?${notificationReferrerId}`) + ); + + it('should generate the GitHub url - enterprise - (pull request)', () => testGenerateUrl( + `${URL.enterprise.api}/pulls/3`, + `${URL.enterprise.default}/pull/3?${notificationReferrerId}`) + ); - it('should generate the GitHub url - enterprise - (release)', () => { - const apiUrl = - 'https://github.gitify.io/api/v3/repos/myorg/notifications-test/releases/1'; + it('should generate the GitHub url - enterprise - (release)', () => testGenerateUrl( + `${URL.enterprise.api}/releases/1`, + `${URL.enterprise.default}/releases?${notificationReferrerId}`) + ); + + it('should generate the GitHub url - enterprise - (discussion)', () => testGenerateUrl( + `${URL.enterprise.api}/discussions/343`, + `${URL.enterprise.default}/discussions/343?${notificationReferrerId}`) + ); + + it('should generate the GitHub issue url with correct commentId', () => testGenerateUrl( + `${URL.normal.api}/issues/5`, + `${URL.normal.default}/issues/5?${notificationReferrerId}#issuecomment-1059824632`, + '#issuecomment-' + getCommentId(`${URL.normal.api}/issues/comments/1059824632`)) + ); + + it('should generate the GitHub discussion url with correct commentId', () => testGenerateUrl( + `${URL.normal.api}/discussions/75`, + `${URL.normal.default}/discussions/75?${notificationReferrerId}#discussioncomment-2300902`, + '#discussioncomment-' + getLatestDiscussionCommentId(mockedGraphQLResponse.data.data.search.edges[0].node.comments.edges)) + ); + + + function testGenerateUrl(apiUrl, ExpectedResult, comment? ) { const notif = { ...mockedSingleNotification, subject: { url: apiUrl } }; - const newUrl = generateGitHubWebUrl( - notif.subject.url, - notif.id, - mockedUser.id - ); - expect(newUrl).toBe( - `https://github.gitify.io/myorg/notifications-test/releases?${notificationReferrerId}` - ); - }); + expect( + generateGitHubWebUrl( + notif.subject.url, + notif.id, + mockedUser.id, + comment) + ).toBe(ExpectedResult); + } }); describe('generateGitHubAPIUrl', () => { diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index fca771ac5..c4e2f1c07 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,7 +1,7 @@ -import { EnterpriseAccount } from '../types'; -import { Notification } from '../typesGithub'; +import { EnterpriseAccount, AuthState } from '../types'; +import { Notification, GraphQLSearch, DiscussionCommentEdge } from '../typesGithub'; import { apiRequestAuth } from '../utils/api-requests'; - +import { openExternalLink } from '../utils/comms'; import { Constants } from './constants'; export function getEnterpriseAccountToken( @@ -31,7 +31,8 @@ export function generateNotificationReferrerId( export function generateGitHubWebUrl( url: string, notificationId: string, - userId?: number + userId?: number, + comment: string = '' ) { const { hostname } = new URL(url); const isEnterprise = @@ -56,10 +57,10 @@ export function generateGitHubWebUrl( userId ); - return `${newUrl}?${notificationReferrerId}`; + return `${newUrl}?${notificationReferrerId}${comment}`; } - return newUrl; + return newUrl + comment; } const addHours = (date: string, hours: number) => @@ -68,11 +69,11 @@ const addHours = (date: string, hours: number) => const queryString = (repo: string, title: string, lastUpdated: string) => `${title} in:title repo:${repo} updated:>${addHours(lastUpdated, -2)}`; -export async function getDiscussionUrl( +async function getDiscussionUrl( notification: Notification, token: string -): Promise { - const response = await apiRequestAuth(`https://api.github.com/graphql`, 'POST', token, { +): Promise<{ url: string, latestCommentId: string|number}> { + const response: GraphQLSearch = await apiRequestAuth(`https://api.github.com/graphql`, 'POST', token, { query: `{ search(query:"${queryString( notification.repository.full_name, @@ -85,6 +86,22 @@ export async function getDiscussionUrl( viewerSubscription title url + comments(last: 100) { + edges { + node { + databaseId + createdAt + replies(last: 1) { + edges { + node { + databaseId + createdAt + } + } + } + } + } + } } } } @@ -98,5 +115,49 @@ export async function getDiscussionUrl( edges = edges.filter( edge => edge.node.viewerSubscription === 'SUBSCRIBED' ); - return edges[0]?.node.url; + + let comments = edges[0]?.node.comments.edges + + let latestCommentId: string|number; + if (comments?.length) { + latestCommentId = getLatestDiscussionCommentId(comments); + } + + return { + url: edges[0]?.node.url, + latestCommentId + } +} + +export const getLatestDiscussionCommentId = (comments: DiscussionCommentEdge[]) => comments + .flatMap(comment => comment.node.replies.edges) + .concat([comments.at(-1)]) + .reduce((a, b) => a.node.createdAt > b.node.createdAt ? a : b) + ?.node.databaseId; + +export const getCommentId = (url: string) => /comments\/(?\d+)/g.exec(url)?.groups?.id; + +export async function openInBrowser(notification: Notification, accounts: AuthState) { + if (notification.subject.url) { + const latestCommentId = getCommentId(notification.subject.latest_comment_url); + openExternalLink( + generateGitHubWebUrl( + notification.subject.url, + notification.id, + accounts.user?.id, + latestCommentId ? '#issuecomment-' + latestCommentId : undefined + ) + ); + } else if (notification.subject.type === 'Discussion') { + getDiscussionUrl(notification, accounts.token).then(({ url, latestCommentId }) => + openExternalLink( + generateGitHubWebUrl( + url || `${notification.repository.url}/discussions`, + notification.id, + accounts.user?.id, + latestCommentId ? '#discussioncomment-' + latestCommentId : undefined + ) + ) + ); + } } diff --git a/src/utils/notifications.test.ts b/src/utils/notifications.test.ts index 87def9d5a..238b7e500 100644 --- a/src/utils/notifications.test.ts +++ b/src/utils/notifications.test.ts @@ -1,6 +1,6 @@ import * as _ from 'lodash'; -import { generateGitHubWebUrl } from './helpers'; +import { generateGitHubWebUrl, getCommentId } from './helpers'; import { mockedAccountNotifications, mockedGithubNotifications, @@ -115,7 +115,8 @@ describe('utils/notifications.ts', () => { const newUrl = generateGitHubWebUrl( notif.subject.url, notif.id, - mockedUser.id + mockedUser.id, + '#issuecomment-' + getCommentId(notif.subject.latest_comment_url) ); expect(comms.openExternalLink).toHaveBeenCalledTimes(1); expect(comms.openExternalLink).toHaveBeenCalledWith(newUrl); diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 2d9850130..8bb980995 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -1,7 +1,7 @@ const { remote } = require('electron'); -import { generateGitHubWebUrl, getDiscussionUrl } from './helpers'; -import { reOpenWindow, openExternalLink, updateTrayIcon } from './comms'; +import { openInBrowser } from '../utils/helpers'; +import { reOpenWindow, updateTrayIcon } from './comms'; import { Notification } from '../typesGithub'; import { AccountNotifications, SettingsState, AuthState } from '../types'; @@ -65,13 +65,11 @@ export const raiseNativeNotification = ( ) => { let title: string; let body: string; - let notificationUrl: string | null; if (notifications.length === 1) { const notification = notifications[0]; title = `Gitify - ${notification.repository.full_name}`; body = notification.subject.title; - notificationUrl = notification.subject.url; } else { title = 'Gitify'; body = `You have ${notifications.length} notifications.`; @@ -84,26 +82,8 @@ export const raiseNativeNotification = ( nativeNotification.onclick = function () { if (notifications.length === 1) { - const appWindow = remote.getCurrentWindow(); - appWindow.hide(); - - const { subject, id, repository } = notifications[0]; - - // Some Notification types from GitHub are missing urls in their subjects. - if (notificationUrl) { - const url = generateGitHubWebUrl(subject.url, id, accounts.user?.id); - openExternalLink(url); - } else if (subject.type === 'Discussion') { - getDiscussionUrl(notifications[0], accounts.token).then(url => - openExternalLink( - generateGitHubWebUrl( - url || `${repository.url}/discussions`, - id, - accounts.user?.id - ) - ) - ); - } + remote.getCurrentWindow().hide(); + openInBrowser(notifications[0], accounts); } else { reOpenWindow(); } diff --git a/tsconfig.json b/tsconfig.json index 1dbc5dddd..2bee74dea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "module": "es6", "moduleResolution": "node", - "target": "es5", + "target": "es2022", "outDir": "./build/", "sourceMap": true, "noImplicitAny": false, diff --git a/yarn.lock b/yarn.lock index ffe5190b2..6a1e934db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -367,14 +367,6 @@ dir-compare "^2.4.0" fs-extra "^9.0.1" -"@fullhuman/postcss-purgecss@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.0.0.tgz#e39bf7a7d2a2c664ed151b639785b2efcbca33ff" - integrity sha512-cvuOgMwIVlfgWcUMqg5p33NbGUxLwMrKtDKkm3QRfOo4PRVNR6+y/xd9OyXTVZiB1bIpKNJ0ZObYPWD3DRQDtw== - dependencies: - postcss "7.0.32" - purgecss "^3.0.0" - "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -579,6 +571,27 @@ lodash "^4.17.15" tmp-promise "^3.0.2" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@primer/octicons-react@^11.2.0": version "11.2.0" resolved "https://registry.yarnpkg.com/@primer/octicons-react/-/octicons-react-11.2.0.tgz#af60301257cdbc1561c2647678282d3bce5a6c54" @@ -1231,6 +1244,14 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + app-builder-bin@3.5.13: version "3.5.13" resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.5.13.tgz#6dd7f4de34a4e408806f99b8c7d6ef1601305b7e" @@ -1265,6 +1286,11 @@ app-builder-lib@22.11.7: semver "^7.3.5" temp-file "^3.4.0" +arg@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.1.tgz#eb0c9a8f77786cad2af8ff2b862899842d7b6adb" + integrity sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1489,6 +1515,11 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + bluebird-lst@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz#a64a0e4365658b9ab5fe875eb9dfb694189bb41c" @@ -1544,7 +1575,7 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1660,9 +1691,9 @@ builder-util@22.11.7: temp-file "^3.4.0" bytes@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== cache-base@^1.0.1: version "1.0.1" @@ -1734,7 +1765,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1751,7 +1782,7 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.1.1: +chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1764,6 +1795,21 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chokidar@^3.5.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chrome-trace-event@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" @@ -1866,7 +1912,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0, color-convert@^1.9.1: +color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -1890,21 +1936,21 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.5.4: - version "1.5.4" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6" - integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw== +color-string@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa" + integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" -color@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e" - integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ== +color@^4.0.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.1.tgz#498aee5fce7fc982606c8875cab080ac0547c884" + integrity sha512-MFJr0uY4RvTQUKvPq7dh9grVOTYSFeXja2mBXioCGjnjJoXrAp9jJ1NQTDR73c9nwBSAQiNKloKl5zq9WB9UPw== dependencies: - color-convert "^1.9.1" - color-string "^1.5.4" + color-convert "^2.0.1" + color-string "^1.9.0" colorette@^1.2.1: version "1.2.1" @@ -1950,11 +1996,16 @@ commander@^5.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== -commander@^6.0.0, commander@^6.2.0: +commander@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== +commander@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -2033,6 +2084,17 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + crc@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" @@ -2065,6 +2127,11 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= + css-loader@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.1.tgz#9e4de0d6636a6266a585bd0900b422c85539d25f" @@ -2264,10 +2331,10 @@ detective@^5.2.0: defined "^1.0.0" minimist "^1.1.1" -didyoumean@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.1.tgz#e92edfdada6537d484d73c0172fd1eba0c4976ff" - integrity sha1-6S7f2tplN9SE1zwBcv0eugxJdv8= +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== diff-sequences@^26.6.2: version "26.6.2" @@ -2284,6 +2351,11 @@ dir-compare@^2.4.0: commander "2.9.0" minimatch "3.0.4" +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + dmg-builder@22.11.7: version "22.11.7" resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.11.7.tgz#5956008c18d40ee72c0ea01ffea9590dbf51df89" @@ -2480,9 +2552,9 @@ end-of-stream@^1.1.0: once "^1.4.0" enhanced-resolve@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz#5d43bda4a0fd447cb0ebbe71bef8deff8805ad0d" - integrity sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ== + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== dependencies: graceful-fs "^4.1.2" memory-fs "^0.5.0" @@ -2514,9 +2586,9 @@ envinfo@^7.7.3: integrity sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA== errno@^0.1.3: - version "0.1.7" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" - integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== dependencies: prr "~1.0.1" @@ -2731,6 +2803,17 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-glob@^3.2.7: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -2741,6 +2824,13 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + fb-watchman@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" @@ -2886,6 +2976,11 @@ fsevents@^2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2932,12 +3027,26 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -2949,6 +3058,18 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.7: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-agent@^2.0.2: version "2.1.12" resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.1.12.tgz#e4ae3812b731a9e81cbf825f9377ef450a8e4195" @@ -3089,6 +3210,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" @@ -3120,6 +3246,16 @@ hosted-git-info@^4.0.2: dependencies: lru-cache "^6.0.0" +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -3276,6 +3412,13 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -3295,6 +3438,18 @@ is-ci@^3.0.0: dependencies: ci-info "^3.1.1" +is-color-stop@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + is-core-module@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz#a4cc031d9b1aca63eecbd18a650e13cb4eeab946" @@ -3302,6 +3457,13 @@ is-core-module@^2.1.0: dependencies: has "^1.0.3" +is-core-module@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -3351,6 +3513,11 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -3366,6 +3533,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-installed-globally@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" @@ -4000,13 +4174,6 @@ json5@2.x, json5@^2.1.2: dependencies: minimist "^1.2.5" -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - json5@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" @@ -4111,6 +4278,11 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lilconfig@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" + integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -4121,15 +4293,6 @@ loader-runner@^4.1.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.1.0.tgz#f70bc0c29edbabdf2043e7ee73ccc3fe1c96b42d" integrity sha512-oR4lB4WvwFoC70ocraKhn5nkKSs23t57h9udUgw8o0iH8hMXeEoRuUgfcvgUwAJ1ZpRqBvcou4N2SMvM1DwMrA== -loader-utils@^1.0.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - loader-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" @@ -4178,10 +4341,10 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash.toarray@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" - integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= +lodash.topath@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" + integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= lodash@^4.17.10, lodash@^4.17.13, lodash@^4.17.15: version "4.17.15" @@ -4193,6 +4356,11 @@ lodash@^4.17.19, lodash@^4.17.20: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -4280,6 +4448,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -4307,6 +4480,14 @@ micromatch@^4.0.0, micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.0.5" +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + mime-db@1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" @@ -4374,10 +4555,10 @@ mkdirp@^0.5.4: dependencies: minimist "^1.2.5" -modern-normalize@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.0.0.tgz#539d84a1e141338b01b346f3e27396d0ed17601e" - integrity sha512-1lM+BMLGuDfsdwf3rsgBSrxJwAZHFIrQ8YR61xIqdHo0uNKI9M52wNpHSrliZATJp51On6JD0AfRxd4YGSU0lw== +modern-normalize@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7" + integrity sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA== ms@2.0.0: version "2.0.0" @@ -4394,6 +4575,11 @@ nanoid@^3.1.20: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -4441,12 +4627,12 @@ node-addon-api@^1.6.3: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== -node-emoji@^1.8.1: - version "1.10.0" - resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" - integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== +node-emoji@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" + integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== dependencies: - lodash.toarray "^4.4.0" + lodash "^4.17.21" node-int64@^0.4.0: version "0.4.0" @@ -4492,7 +4678,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -4558,10 +4744,10 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-hash@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" - integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== +object-hash@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== object-keys@^1.0.12: version "1.1.1" @@ -4718,6 +4904,11 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -4740,11 +4931,21 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.0.5: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +picomatch@^2.2.1, picomatch@^2.2.3: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -4785,16 +4986,6 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -postcss-functions@^3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-functions/-/postcss-functions-3.0.0.tgz#0e94d01444700a481de20de4d55fb2640564250e" - integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4= - dependencies: - glob "^7.1.2" - object-assign "^4.1.1" - postcss "^6.0.9" - postcss-value-parser "^3.3.0" - postcss-js@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-3.0.3.tgz#2f0bd370a2e8599d45439f6970403b5873abda33" @@ -4803,6 +4994,14 @@ postcss-js@^3.0.3: camelcase-css "^2.0.1" postcss "^8.1.6" +postcss-load-config@^3.1.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.3.tgz#21935b2c43b9a86e6581a576ca7ee1bde2bd1d23" + integrity sha512-5EYgaM9auHGtO//ljHH+v/aC/TQ5LHXtL7bQajNAUBKUVKiYE8rYpFms7+V26D9FncaGe2zwCoPQsFKb5zF/Hw== + dependencies: + lilconfig "^2.0.4" + yaml "^1.10.2" + postcss-loader@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.1.0.tgz#4647a6c8dad3cb6b253fbfaa21d62201086f6e39" @@ -4842,12 +5041,12 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-nested@^5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.3.tgz#2f46d77a06fc98d9c22344fd097396f5431386db" - integrity sha512-R2LHPw+u5hFfDgJG748KpGbJyTv7Yr33/2tIMWxquYuHTd9EXu27PYnKi7BxMXLtzKC0a0WVsqHtd7qIluQu/g== +postcss-nested@5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.6.tgz#466343f7fc8d3d46af3e7dba3fcd47d052a945bc" + integrity sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA== dependencies: - postcss-selector-parser "^6.0.4" + postcss-selector-parser "^6.0.6" postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.4" @@ -4859,6 +5058,14 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: uniq "^1.0.1" util-deprecate "^1.0.2" +postcss-selector-parser@^6.0.6: + version "6.0.9" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" + integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-value-parser@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" @@ -4869,25 +5076,7 @@ postcss-value-parser@^4.1.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -postcss@7.0.32: - version "7.0.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" - integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -postcss@^6.0.9: - version "6.0.23" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" - integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== - dependencies: - chalk "^2.4.1" - source-map "^0.6.1" - supports-color "^5.4.0" - -postcss@^8.1.4, postcss@^8.1.6, postcss@^8.2.1: +postcss@^8.1.4, postcss@^8.2.1: version "8.2.1" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.1.tgz#eabc5557c4558059b9d9e5b15bce7ffa9089c2a8" integrity sha512-RhsqOOAQzTgh1UB/IZdca7F9WDb7SUCR2Vnv1x7DbvuuggQIpoDwjK+q0rzoPffhYvWNKX5JSwS4so4K3UC6vA== @@ -4896,6 +5085,15 @@ postcss@^8.1.4, postcss@^8.1.6, postcss@^8.2.1: nanoid "^3.1.20" source-map "^0.6.1" +postcss@^8.1.6, postcss@^8.3.5: + version "8.4.8" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.8.tgz#dad963a76e82c081a0657d3a2f3602ce10c2e032" + integrity sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ== + dependencies: + nanoid "^3.3.1" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -4993,21 +5191,31 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" -purgecss@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-3.0.0.tgz#039c191871bb999894222a00c4c8b179fccdb043" - integrity sha512-t3FGCwyX9XWV3ffvnAXTw6Y3Z9kNlcgm14VImNK66xKi5sdqxSA2I0SFYxtmZbAKuIZVckPdazw5iKL/oY/2TA== +purgecss@^4.0.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-4.1.3.tgz#683f6a133c8c4de7aa82fe2746d1393b214918f7" + integrity sha512-99cKy4s+VZoXnPxaoM23e5ABcP851nC2y2GROkkjS8eJaJtlciGavd7iYAw2V84WeBqggZ12l8ef44G99HmTaw== dependencies: - commander "^6.0.0" - glob "^7.0.0" - postcss "7.0.32" - postcss-selector-parser "^6.0.2" + commander "^8.0.0" + glob "^7.1.7" + postcss "^8.3.5" + postcss-selector-parser "^6.0.6" qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -5160,6 +5368,13 @@ readable-stream@^2.0.1, readable-stream@^2.2.2: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + rechoir@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca" @@ -5167,10 +5382,10 @@ rechoir@^0.7.0: dependencies: resolve "^1.9.0" -reduce-css-calc@^2.1.6: - version "2.1.7" - resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz#1ace2e02c286d78abcd01fd92bfe8097ab0602c2" - integrity sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA== +reduce-css-calc@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz#7ef8761a28d614980dc0c982f772c93f7a99de03" + integrity sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg== dependencies: css-unit-converter "^1.1.1" postcss-value-parser "^3.3.0" @@ -5308,7 +5523,7 @@ resolve@^1.10.0, resolve@^1.3.2: dependencies: path-parse "^1.0.6" -resolve@^1.18.1, resolve@^1.19.0, resolve@^1.9.0: +resolve@^1.18.1, resolve@^1.9.0: version "1.19.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== @@ -5316,6 +5531,15 @@ resolve@^1.18.1, resolve@^1.19.0, resolve@^1.9.0: is-core-module "^2.1.0" path-parse "^1.0.6" +resolve@^1.20.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + dependencies: + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -5328,6 +5552,21 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + rimraf@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -5352,6 +5591,13 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -5600,6 +5846,11 @@ source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -5819,20 +6070,13 @@ sumchecker@^3.0.1: dependencies: debug "^4.1.0" -supports-color@^5.3.0, supports-color@^5.4.0: +supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - supports-color@^7.0.0, supports-color@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" @@ -5848,6 +6092,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -5864,30 +6113,42 @@ table-layout@^1.0.1: wordwrapjs "^4.0.0" tailwindcss@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.0.2.tgz#28e1573d29dd4547b26782facb05bcfaa92be366" - integrity sha512-nO9JRE1pO7SF9RnYAl6g7uzeHdrmKAFqNjT9NtZUfxqimJZAOOLOEyIEUiMq12+xIc7mC2Ey3Vf90XjHpWKfbw== + version "2.2.19" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.19.tgz#540e464832cd462bb9649c1484b0a38315c2653c" + integrity sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw== dependencies: - "@fullhuman/postcss-purgecss" "^3.0.0" + arg "^5.0.1" bytes "^3.0.0" - chalk "^4.1.0" - color "^3.1.3" + chalk "^4.1.2" + chokidar "^3.5.2" + color "^4.0.1" + cosmiconfig "^7.0.1" detective "^5.2.0" - didyoumean "^1.2.1" - fs-extra "^9.0.1" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.2.7" + fs-extra "^10.0.0" + glob-parent "^6.0.1" html-tags "^3.1.0" - lodash "^4.17.20" - modern-normalize "^1.0.0" - node-emoji "^1.8.1" - object-hash "^2.0.3" - postcss-functions "^3" + is-color-stop "^1.1.0" + is-glob "^4.0.1" + lodash "^4.17.21" + lodash.topath "^4.5.2" + modern-normalize "^1.1.0" + node-emoji "^1.11.0" + normalize-path "^3.0.0" + object-hash "^2.2.0" postcss-js "^3.0.3" - postcss-nested "^5.0.1" - postcss-selector-parser "^6.0.4" + postcss-load-config "^3.1.0" + postcss-nested "5.0.6" + postcss-selector-parser "^6.0.6" postcss-value-parser "^4.1.0" pretty-hrtime "^1.0.3" - reduce-css-calc "^2.1.6" - resolve "^1.19.0" + purgecss "^4.0.3" + quick-lru "^5.1.1" + reduce-css-calc "^2.1.8" + resolve "^1.20.0" + tmp "^0.2.1" tapable@^1.0.0: version "1.1.3" @@ -5972,7 +6233,7 @@ tmp-promise@^3.0.2: dependencies: tmp "^0.2.0" -tmp@^0.2.0: +tmp@^0.2.0, tmp@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== @@ -6080,15 +6341,15 @@ ts-jest@^26.4.4: yargs-parser "20.x" ts-loader@^8.0.12: - version "8.0.12" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.12.tgz#1de9f1de65176318c1e6d187bfc496182f8dc2a0" - integrity sha512-UIivVfGVJDdwwjgSrbtcL9Nf10c1BWnL1mxAQUVcnhNIn/P9W3nP5v60Z0aBMtc7ZrE11lMmU6+5jSgAXmGaYw== + version "8.3.0" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.3.0.tgz#83360496d6f8004fab35825279132c93412edf33" + integrity sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag== dependencies: - chalk "^2.3.0" + chalk "^4.1.0" enhanced-resolve "^4.0.0" - loader-utils "^1.0.2" + loader-utils "^2.0.0" micromatch "^4.0.0" - semver "^6.0.0" + semver "^7.3.4" tslib@^1.9.0: version "1.13.0" @@ -6161,10 +6422,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" - integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== +typescript@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" + integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== typical@^5.0.0, typical@^5.2.0: version "5.2.0" @@ -6585,6 +6846,11 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== +yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yargs-parser@20.x, yargs-parser@^20.2.2: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" From 7b1191a9fe145cc3545893ebe345d1c74e75068b Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Thu, 14 Sep 2023 02:51:39 +0100 Subject: [PATCH 6/7] chore: push updated lockfile --- pnpm-lock.yaml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 749834904..ddbfa5106 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,7 +49,7 @@ dependencies: version: 5.0.1(react-dom@16.14.0)(react@16.14.0) react-final-form: specifier: ^6.4.0 - version: 6.4.0(final-form@4.20.1)(react@16.14.0)(typescript@4.1.3) + version: 6.4.0(final-form@4.20.1)(react@16.14.0)(typescript@4.6.2) react-router: specifier: ^5.3.4 version: 5.3.4(react@16.14.0) @@ -64,10 +64,10 @@ dependencies: version: 2.0.2(autoprefixer@10.1.0)(postcss@8.4.23) ts-loader: specifier: ^9.4.2 - version: 9.4.2(typescript@4.1.3)(webpack@5.83.1) + version: 9.4.2(typescript@4.6.2)(webpack@5.83.1) typescript: - specifier: ^4.1.3 - version: 4.1.3 + specifier: ^4.6.2 + version: 4.6.2 devDependencies: '@testing-library/react': @@ -129,7 +129,7 @@ devDependencies: version: 3.3.3(webpack@5.83.1) ts-jest: specifier: ^26.4.4 - version: 26.4.4(jest@26.6.3)(typescript@4.1.3) + version: 26.4.4(jest@26.6.3)(typescript@4.6.2) webpack: specifier: ^5.83.1 version: 5.83.1(webpack-cli@5.1.1) @@ -4969,7 +4969,7 @@ packages: react-dom: 16.14.0(react@16.14.0) dev: false - /react-final-form@6.4.0(final-form@4.20.1)(react@16.14.0)(typescript@4.1.3): + /react-final-form@6.4.0(final-form@4.20.1)(react@16.14.0)(typescript@4.6.2): resolution: {integrity: sha512-M7J7f0pnoj0o8sBq3iG6jsWJEh08pNUyl2D4wBC9SJvCNkGdol2UdyjMiEFYD3rz9LIFzQqFSG0kbRBCadqzhA==} peerDependencies: final-form: ^4.19.0 @@ -4978,7 +4978,7 @@ packages: '@babel/runtime': 7.21.5 final-form: 4.20.1 react: 16.14.0 - ts-essentials: 6.0.7(typescript@4.1.3) + ts-essentials: 6.0.7(typescript@4.6.2) transitivePeerDependencies: - typescript dev: false @@ -5879,15 +5879,15 @@ packages: utf8-byte-length: 1.0.4 dev: true - /ts-essentials@6.0.7(typescript@4.1.3): + /ts-essentials@6.0.7(typescript@4.6.2): resolution: {integrity: sha512-2E4HIIj4tQJlIHuATRHayv0EfMGK3ris/GRk1E3CFnsZzeNV+hUmelbaTZHLtXaZppM5oLhHRtO04gINC4Jusw==} peerDependencies: typescript: '>=3.7.0' dependencies: - typescript: 4.1.3 + typescript: 4.6.2 dev: false - /ts-jest@26.4.4(jest@26.6.3)(typescript@4.1.3): + /ts-jest@26.4.4(jest@26.6.3)(typescript@4.6.2): resolution: {integrity: sha512-3lFWKbLxJm34QxyVNNCgXX1u4o/RV0myvA2y2Bxm46iGIjKlaY0own9gIckbjZJPn+WaJEnfPPJ20HHGpoq4yg==} engines: {node: '>= 10'} hasBin: true @@ -5906,11 +5906,11 @@ packages: make-error: 1.3.6 mkdirp: 1.0.4 semver: 7.5.1 - typescript: 4.1.3 + typescript: 4.6.2 yargs-parser: 20.2.9 dev: true - /ts-loader@9.4.2(typescript@4.1.3)(webpack@5.83.1): + /ts-loader@9.4.2(typescript@4.6.2)(webpack@5.83.1): resolution: {integrity: sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==} engines: {node: '>=12.0.0'} peerDependencies: @@ -5921,7 +5921,7 @@ packages: enhanced-resolve: 5.14.0 micromatch: 4.0.5 semver: 7.5.1 - typescript: 4.1.3 + typescript: 4.6.2 webpack: 5.83.1(webpack-cli@5.1.1) dev: false @@ -5981,8 +5981,8 @@ packages: /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - /typescript@4.1.3: - resolution: {integrity: sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==} + /typescript@4.6.2: + resolution: {integrity: sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==} engines: {node: '>=4.2.0'} hasBin: true From aa7bba3adda3d76b8b86f918113c37b3b98600d6 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Thu, 14 Sep 2023 02:54:59 +0100 Subject: [PATCH 7/7] style: prettify files --- src/__mocks__/electron.js | 8 +- src/__mocks__/mockedData.ts | 220 ++++++++++++++--------------- src/components/NotificationRow.tsx | 13 +- src/hooks/useNotifications.ts | 7 +- src/routes/LoginWithToken.tsx | 6 +- src/typesGithub.ts | 31 ++-- src/utils/auth.test.ts | 26 ++-- src/utils/helpers.test.ts | 140 ++++++++++-------- src/utils/helpers.ts | 84 +++++++---- 9 files changed, 283 insertions(+), 252 deletions(-) diff --git a/src/__mocks__/electron.js b/src/__mocks__/electron.js index 97bf91c0f..83fd9ad4c 100644 --- a/src/__mocks__/electron.js +++ b/src/__mocks__/electron.js @@ -31,7 +31,7 @@ let instance; class BrowserWindow { constructor() { - if(!instance){ + if (!instance) { instance = this; } return instance; @@ -43,11 +43,11 @@ class BrowserWindow { clearStorageData: jest.fn(), }, }; - on(){}; + on() {} close = jest.fn(); hide = jest.fn(); destroy = jest.fn(); -}; +} const dialog = { showErrorBox: jest.fn(), @@ -65,7 +65,7 @@ module.exports = { getLoginItemSettings: jest.fn(), setLoginItemSettings: () => {}, }, - getCurrentWindow: jest.fn(() => instance || new BrowserWindow), + getCurrentWindow: jest.fn(() => instance || new BrowserWindow()), }, ipcRenderer: { send: jest.fn(), diff --git a/src/__mocks__/mockedData.ts b/src/__mocks__/mockedData.ts index 08b94e0b5..ab2616d3f 100644 --- a/src/__mocks__/mockedData.ts +++ b/src/__mocks__/mockedData.ts @@ -276,114 +276,114 @@ export const mockedSingleAccountNotifications: AccountNotifications[] = [ ]; export const mockedGraphQLResponse: GraphQLSearch = { - "data": { - "data": { - "search": { - "edges": [ + data: { + data: { + search: { + edges: [ + { + node: { + viewerSubscription: 'SUBSCRIBED', + title: '1.16.0', + url: 'https://github.com/manosim/notifications-test/discussions/612', + comments: { + edges: [ { - "node": { - "viewerSubscription": "SUBSCRIBED", - "title": "1.16.0", - "url": "https://github.com/manosim/notifications-test/discussions/612", - "comments": { - "edges": [ - { - "node": { - "databaseId": 2215656, - "createdAt": "2022-02-20T18:33:39Z", - "replies": { - "edges": [] - } - } - }, - { - "node": { - "databaseId": 2217789, - "createdAt": "2022-02-21T03:30:42Z", - "replies": { - "edges": [] - } - } - }, - { - "node": { - "databaseId": 2223243, - "createdAt": "2022-02-21T18:26:27Z", - "replies": { - "edges": [ - { - "node": { - "databaseId": 2232922, - "createdAt": "2022-02-23T00:57:58Z" - } - } - ] - } - } - }, - { - "node": { - "databaseId": 2232921, - "createdAt": "2022-02-23T00:57:49Z", - "replies": { - "edges": [] - } - } - }, - { - "node": { - "databaseId": 2258799, - "createdAt": "2022-02-27T01:22:20Z", - "replies": { - "edges": [ - { - "node": { - "databaseId": 2300902, - "createdAt": "2022-03-05T17:43:52Z" - } - } - ] - } - } - }, - { - "node": { - "databaseId": 2297637, - "createdAt": "2022-03-04T20:39:44Z", - "replies": { - "edges": [ - { - "node": { - "databaseId": 2300893, - "createdAt": "2022-03-05T17:41:04Z" - } - } - ] - } - } - }, - { - "node": { - "databaseId": 2299763, - "createdAt": "2022-03-05T11:05:42Z", - "replies": { - "edges": [ - { - "node": { - "databaseId": 2300895, - "createdAt": "2022-03-05T17:41:44Z" - } - } - ] - } - } - } - ] - } - } - } - ] - } - } - } -} + node: { + databaseId: 2215656, + createdAt: '2022-02-20T18:33:39Z', + replies: { + edges: [], + }, + }, + }, + { + node: { + databaseId: 2217789, + createdAt: '2022-02-21T03:30:42Z', + replies: { + edges: [], + }, + }, + }, + { + node: { + databaseId: 2223243, + createdAt: '2022-02-21T18:26:27Z', + replies: { + edges: [ + { + node: { + databaseId: 2232922, + createdAt: '2022-02-23T00:57:58Z', + }, + }, + ], + }, + }, + }, + { + node: { + databaseId: 2232921, + createdAt: '2022-02-23T00:57:49Z', + replies: { + edges: [], + }, + }, + }, + { + node: { + databaseId: 2258799, + createdAt: '2022-02-27T01:22:20Z', + replies: { + edges: [ + { + node: { + databaseId: 2300902, + createdAt: '2022-03-05T17:43:52Z', + }, + }, + ], + }, + }, + }, + { + node: { + databaseId: 2297637, + createdAt: '2022-03-04T20:39:44Z', + replies: { + edges: [ + { + node: { + databaseId: 2300893, + createdAt: '2022-03-05T17:41:04Z', + }, + }, + ], + }, + }, + }, + { + node: { + databaseId: 2299763, + createdAt: '2022-03-05T11:05:42Z', + replies: { + edges: [ + { + node: { + databaseId: 2300895, + createdAt: '2022-03-05T17:41:44Z', + }, + }, + ], + }, + }, + }, + ], + }, + }, + }, + ], + }, + }, + }, +}; diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx index 9baff8237..199bd3c60 100644 --- a/src/components/NotificationRow.tsx +++ b/src/components/NotificationRow.tsx @@ -16,12 +16,8 @@ export const NotificationRow: React.FC = ({ notification, hostname, }) => { - const { - settings, - accounts, - markNotification, - unsubscribeNotification, - } = useContext(AppContext); + const { settings, accounts, markNotification, unsubscribeNotification } = + useContext(AppContext); const pressTitle = useCallback(() => { openBrowser(); @@ -31,7 +27,10 @@ export const NotificationRow: React.FC = ({ } }, [settings]); - const openBrowser = useCallback(() => openInBrowser(notification, accounts), [notification]); + const openBrowser = useCallback( + () => openInBrowser(notification, accounts), + [notification] + ); const unsubscribe = (event: React.MouseEvent) => { // Don't trigger onClick of parent element. diff --git a/src/hooks/useNotifications.ts b/src/hooks/useNotifications.ts index e44068392..b6172c79f 100644 --- a/src/hooks/useNotifications.ts +++ b/src/hooks/useNotifications.ts @@ -96,12 +96,7 @@ export const useNotifications = (): NotificationsState => { ] : [...enterpriseNotifications]; - triggerNativeNotifications( - notifications, - data, - settings, - accounts - ); + triggerNativeNotifications(notifications, data, settings, accounts); setNotifications(data); setIsFetching(false); }) diff --git a/src/routes/LoginWithToken.tsx b/src/routes/LoginWithToken.tsx index 59d9d4807..ab1659368 100644 --- a/src/routes/LoginWithToken.tsx +++ b/src/routes/LoginWithToken.tsx @@ -63,7 +63,11 @@ export const LoginWithToken: React.FC = () => { To generate a token, go to GitHub,{' '} openLink('https://github.com/settings/tokens/new?scopes=notifications,read:user&description=gitify_token')} + onClick={() => + openLink( + 'https://github.com/settings/tokens/new?scopes=notifications,read:user&description=gitify_token' + ) + } > personal access tokens {' '} diff --git a/src/typesGithub.ts b/src/typesGithub.ts index a8c23999d..3f69440c3 100644 --- a/src/typesGithub.ts +++ b/src/typesGithub.ts @@ -22,10 +22,7 @@ export type SubjectType = | 'RepositoryInvitation' | 'RepositoryVulnerabilityAlert'; -export type ViewerSubscription = - | 'IGNORED' - | 'SUBSCRIBED' - | 'UNSUBSCRIBED' +export type ViewerSubscription = 'IGNORED' | 'SUBSCRIBED' | 'UNSUBSCRIBED'; export interface Notification { id: string; @@ -126,10 +123,10 @@ export interface GraphQLSearch { data: { data: { search: { - edges: DiscussionEdge[] - } - } - } + edges: DiscussionEdge[]; + }; + }; + }; } export interface DiscussionEdge { @@ -138,24 +135,24 @@ export interface DiscussionEdge { title: string; url: string; comments: { - edges: DiscussionCommentEdge[] - } - } + edges: DiscussionCommentEdge[]; + }; + }; } export interface DiscussionCommentEdge { node: { - databaseId: string|number; + databaseId: string | number; createdAt: string; replies: { - edges: DiscussionSubcommentEdge[] - } - } + edges: DiscussionSubcommentEdge[]; + }; + }; } export interface DiscussionSubcommentEdge { node: { - databaseId: string|number; + databaseId: string | number; createdAt: string; - } + }; } diff --git a/src/utils/auth.test.ts b/src/utils/auth.test.ts index d31708d66..504e91530 100644 --- a/src/utils/auth.test.ts +++ b/src/utils/auth.test.ts @@ -1,7 +1,7 @@ import { AxiosPromise, AxiosResponse } from 'axios'; import { remote } from 'electron'; -const browserWindow = new remote.BrowserWindow +const browserWindow = new remote.BrowserWindow(); import * as auth from './auth'; import * as apiRequests from './api-requests'; @@ -16,14 +16,12 @@ describe('utils/auth.tsx', () => { }); it('should call authGithub - success', async () => { - spyOn(browserWindow.webContents, 'on').and.callFake( - (event, callback) => { - if (event === 'will-redirect') { - const event = new Event('will-redirect'); - callback(event, 'http://github.com/?code=123-456'); - } + spyOn(browserWindow.webContents, 'on').and.callFake((event, callback) => { + if (event === 'will-redirect') { + const event = new Event('will-redirect'); + callback(event, 'http://github.com/?code=123-456'); } - ); + }); const res = await auth.authGitHub(); @@ -42,14 +40,12 @@ describe('utils/auth.tsx', () => { }); it('should call authGithub - failure', async () => { - spyOn(browserWindow.webContents, 'on').and.callFake( - (event, callback) => { - if (event === 'will-redirect') { - const event = new Event('will-redirect'); - callback(event, 'http://www.github.com/?error=Oops'); - } + spyOn(browserWindow.webContents, 'on').and.callFake((event, callback) => { + if (event === 'will-redirect') { + const event = new Event('will-redirect'); + callback(event, 'http://www.github.com/?error=Oops'); } - ); + }); await expect(async () => await auth.authGitHub()).rejects.toEqual( "Oops! Something went wrong and we couldn't log you in using Github. Please try again." diff --git a/src/utils/helpers.test.ts b/src/utils/helpers.test.ts index 1ea265874..6f65f868d 100644 --- a/src/utils/helpers.test.ts +++ b/src/utils/helpers.test.ts @@ -3,20 +3,24 @@ import { generateGitHubAPIUrl, generateNotificationReferrerId, getCommentId, - getLatestDiscussionCommentId + getLatestDiscussionCommentId, } from './helpers'; -import { mockedSingleNotification, mockedUser, mockedGraphQLResponse } from '../__mocks__/mockedData'; +import { + mockedSingleNotification, + mockedUser, + mockedGraphQLResponse, +} from '../__mocks__/mockedData'; const URL = { normal: { api: 'https://api.github.com/repos/myuser/notifications-test', - default: 'https://github.com/myuser/notifications-test' + default: 'https://github.com/myuser/notifications-test', }, enterprise: { api: 'https://github.gitify.io/api/v3/repos/myorg/notifications-test', - default: 'https://github.gitify.io/myorg/notifications-test' - } -} + default: 'https://github.gitify.io/myorg/notifications-test', + }, +}; describe('utils/helpers.ts', () => { describe('generateNotificationReferrerId', () => { @@ -40,67 +44,81 @@ describe('utils/helpers.ts', () => { ); }); - it('should generate the GitHub url - non enterprise - (issue)', () => testGenerateUrl( - `${URL.normal.api}/issues/3`, - `${URL.normal.default}/issues/3?${notificationReferrerId}`) - ); - - it('should generate the GitHub url - non enterprise - (pull request)', () => testGenerateUrl( - `${URL.normal.api}/pulls/123`, - `${URL.normal.default}/pull/123?${notificationReferrerId}`) - ); - - it('should generate the GitHub url - non enterprise - (release)', () => testGenerateUrl( - `${URL.normal.api}/releases/3988077`, - `${URL.normal.default}/releases?${notificationReferrerId}`) - ); - - it('should generate the GitHub url - non enterprise - (discussion)', () => testGenerateUrl( - `${URL.normal.api}/discussions/630`, - `${URL.normal.default}/discussions/630?${notificationReferrerId}`) - ); - - it('should generate the GitHub url - enterprise - (issue)', () => testGenerateUrl( - `${URL.enterprise.api}/issues/123`, - `${URL.enterprise.default}/issues/123?${notificationReferrerId}`) - ); - - it('should generate the GitHub url - enterprise - (pull request)', () => testGenerateUrl( - `${URL.enterprise.api}/pulls/3`, - `${URL.enterprise.default}/pull/3?${notificationReferrerId}`) - ); - - it('should generate the GitHub url - enterprise - (release)', () => testGenerateUrl( - `${URL.enterprise.api}/releases/1`, - `${URL.enterprise.default}/releases?${notificationReferrerId}`) - ); - - it('should generate the GitHub url - enterprise - (discussion)', () => testGenerateUrl( - `${URL.enterprise.api}/discussions/343`, - `${URL.enterprise.default}/discussions/343?${notificationReferrerId}`) - ); - - it('should generate the GitHub issue url with correct commentId', () => testGenerateUrl( - `${URL.normal.api}/issues/5`, - `${URL.normal.default}/issues/5?${notificationReferrerId}#issuecomment-1059824632`, - '#issuecomment-' + getCommentId(`${URL.normal.api}/issues/comments/1059824632`)) - ); - - it('should generate the GitHub discussion url with correct commentId', () => testGenerateUrl( - `${URL.normal.api}/discussions/75`, - `${URL.normal.default}/discussions/75?${notificationReferrerId}#discussioncomment-2300902`, - '#discussioncomment-' + getLatestDiscussionCommentId(mockedGraphQLResponse.data.data.search.edges[0].node.comments.edges)) - ); - - - function testGenerateUrl(apiUrl, ExpectedResult, comment? ) { + it('should generate the GitHub url - non enterprise - (issue)', () => + testGenerateUrl( + `${URL.normal.api}/issues/3`, + `${URL.normal.default}/issues/3?${notificationReferrerId}` + )); + + it('should generate the GitHub url - non enterprise - (pull request)', () => + testGenerateUrl( + `${URL.normal.api}/pulls/123`, + `${URL.normal.default}/pull/123?${notificationReferrerId}` + )); + + it('should generate the GitHub url - non enterprise - (release)', () => + testGenerateUrl( + `${URL.normal.api}/releases/3988077`, + `${URL.normal.default}/releases?${notificationReferrerId}` + )); + + it('should generate the GitHub url - non enterprise - (discussion)', () => + testGenerateUrl( + `${URL.normal.api}/discussions/630`, + `${URL.normal.default}/discussions/630?${notificationReferrerId}` + )); + + it('should generate the GitHub url - enterprise - (issue)', () => + testGenerateUrl( + `${URL.enterprise.api}/issues/123`, + `${URL.enterprise.default}/issues/123?${notificationReferrerId}` + )); + + it('should generate the GitHub url - enterprise - (pull request)', () => + testGenerateUrl( + `${URL.enterprise.api}/pulls/3`, + `${URL.enterprise.default}/pull/3?${notificationReferrerId}` + )); + + it('should generate the GitHub url - enterprise - (release)', () => + testGenerateUrl( + `${URL.enterprise.api}/releases/1`, + `${URL.enterprise.default}/releases?${notificationReferrerId}` + )); + + it('should generate the GitHub url - enterprise - (discussion)', () => + testGenerateUrl( + `${URL.enterprise.api}/discussions/343`, + `${URL.enterprise.default}/discussions/343?${notificationReferrerId}` + )); + + it('should generate the GitHub issue url with correct commentId', () => + testGenerateUrl( + `${URL.normal.api}/issues/5`, + `${URL.normal.default}/issues/5?${notificationReferrerId}#issuecomment-1059824632`, + '#issuecomment-' + + getCommentId(`${URL.normal.api}/issues/comments/1059824632`) + )); + + it('should generate the GitHub discussion url with correct commentId', () => + testGenerateUrl( + `${URL.normal.api}/discussions/75`, + `${URL.normal.default}/discussions/75?${notificationReferrerId}#discussioncomment-2300902`, + '#discussioncomment-' + + getLatestDiscussionCommentId( + mockedGraphQLResponse.data.data.search.edges[0].node.comments.edges + ) + )); + + function testGenerateUrl(apiUrl, ExpectedResult, comment?) { const notif = { ...mockedSingleNotification, subject: { url: apiUrl } }; expect( generateGitHubWebUrl( notif.subject.url, notif.id, mockedUser.id, - comment) + comment + ) ).toBe(ExpectedResult); } }); diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index c4e2f1c07..af3d54a33 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,5 +1,9 @@ import { EnterpriseAccount, AuthState } from '../types'; -import { Notification, GraphQLSearch, DiscussionCommentEdge } from '../typesGithub'; +import { + Notification, + GraphQLSearch, + DiscussionCommentEdge, +} from '../typesGithub'; import { apiRequestAuth } from '../utils/api-requests'; import { openExternalLink } from '../utils/comms'; import { Constants } from './constants'; @@ -72,9 +76,13 @@ const queryString = (repo: string, title: string, lastUpdated: string) => async function getDiscussionUrl( notification: Notification, token: string -): Promise<{ url: string, latestCommentId: string|number}> { - const response: GraphQLSearch = await apiRequestAuth(`https://api.github.com/graphql`, 'POST', token, { - query: `{ +): Promise<{ url: string; latestCommentId: string | number }> { + const response: GraphQLSearch = await apiRequestAuth( + `https://api.github.com/graphql`, + 'POST', + token, + { + query: `{ search(query:"${queryString( notification.repository.full_name, notification.subject.title, @@ -106,40 +114,51 @@ async function getDiscussionUrl( } } } - }` - }); - let edges = response?.data?.data?.search?.edges?.filter( - edge => edge.node.title === notification.subject.title - ) || []; + }`, + } + ); + let edges = + response?.data?.data?.search?.edges?.filter( + (edge) => edge.node.title === notification.subject.title + ) || []; if (edges.length > 1) edges = edges.filter( - edge => edge.node.viewerSubscription === 'SUBSCRIBED' + (edge) => edge.node.viewerSubscription === 'SUBSCRIBED' ); - - let comments = edges[0]?.node.comments.edges - let latestCommentId: string|number; + let comments = edges[0]?.node.comments.edges; + + let latestCommentId: string | number; if (comments?.length) { latestCommentId = getLatestDiscussionCommentId(comments); } return { url: edges[0]?.node.url, - latestCommentId - } + latestCommentId, + }; } -export const getLatestDiscussionCommentId = (comments: DiscussionCommentEdge[]) => comments - .flatMap(comment => comment.node.replies.edges) - .concat([comments.at(-1)]) - .reduce((a, b) => a.node.createdAt > b.node.createdAt ? a : b) - ?.node.databaseId; +export const getLatestDiscussionCommentId = ( + comments: DiscussionCommentEdge[] +) => + comments + .flatMap((comment) => comment.node.replies.edges) + .concat([comments.at(-1)]) + .reduce((a, b) => (a.node.createdAt > b.node.createdAt ? a : b))?.node + .databaseId; -export const getCommentId = (url: string) => /comments\/(?\d+)/g.exec(url)?.groups?.id; +export const getCommentId = (url: string) => + /comments\/(?\d+)/g.exec(url)?.groups?.id; -export async function openInBrowser(notification: Notification, accounts: AuthState) { +export async function openInBrowser( + notification: Notification, + accounts: AuthState +) { if (notification.subject.url) { - const latestCommentId = getCommentId(notification.subject.latest_comment_url); + const latestCommentId = getCommentId( + notification.subject.latest_comment_url + ); openExternalLink( generateGitHubWebUrl( notification.subject.url, @@ -149,15 +168,18 @@ export async function openInBrowser(notification: Notification, accounts: AuthSt ) ); } else if (notification.subject.type === 'Discussion') { - getDiscussionUrl(notification, accounts.token).then(({ url, latestCommentId }) => - openExternalLink( - generateGitHubWebUrl( - url || `${notification.repository.url}/discussions`, - notification.id, - accounts.user?.id, - latestCommentId ? '#discussioncomment-' + latestCommentId : undefined + getDiscussionUrl(notification, accounts.token).then( + ({ url, latestCommentId }) => + openExternalLink( + generateGitHubWebUrl( + url || `${notification.repository.url}/discussions`, + notification.id, + accounts.user?.id, + latestCommentId + ? '#discussioncomment-' + latestCommentId + : undefined + ) ) - ) ); } }