mirror of
https://github.com/zhigang1992/devhub.git
synced 2026-04-28 09:26:24 +08:00
Move more code to the core package
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
EnhancedGitHubEvent,
|
||||
getBranchNameFromRef,
|
||||
getDateSmallText,
|
||||
getEventIconAndColor,
|
||||
getEventMetadata,
|
||||
getFullDateText,
|
||||
getGitHubAvatarURLFromPayload,
|
||||
@@ -43,7 +44,6 @@ import {
|
||||
smallAvatarSize,
|
||||
smallerTextSize,
|
||||
} from '../../styles/variables'
|
||||
import { getEventIconAndColor } from '../../utils/helpers/github/events'
|
||||
import { tryFocus } from '../../utils/helpers/shared'
|
||||
import { getCardBackgroundThemeColor } from '../columns/ColumnRenderer'
|
||||
import { Avatar } from '../common/Avatar'
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getDateSmallText,
|
||||
getFullDateText,
|
||||
getGitHubURLForRepo,
|
||||
getIssueOrPullRequestIconAndColor,
|
||||
getIssueOrPullRequestNumberFromUrl,
|
||||
getOwnerAndRepo,
|
||||
getRepoFullNameFromUrl,
|
||||
@@ -22,7 +23,6 @@ import {
|
||||
smallAvatarSize,
|
||||
smallerTextSize,
|
||||
} from '../../styles/variables'
|
||||
import { getIssueOrPullRequestIconAndColor } from '../../utils/helpers/github/issues'
|
||||
import { tryFocus } from '../../utils/helpers/shared'
|
||||
import { getCardBackgroundThemeColor } from '../columns/ColumnRenderer'
|
||||
import { Avatar } from '../common/Avatar'
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
getGitHubURLForRepoInvitation,
|
||||
getGitHubURLForSecurityAlert,
|
||||
getIssueOrPullRequestNumberFromUrl,
|
||||
getNotificationIconAndColor,
|
||||
getOwnerAndRepo,
|
||||
getUserAvatarByUsername,
|
||||
GitHubLabel,
|
||||
@@ -25,7 +26,6 @@ import {
|
||||
contentPadding,
|
||||
smallerTextSize,
|
||||
} from '../../styles/variables'
|
||||
import { getNotificationIconAndColor } from '../../utils/helpers/github/notifications'
|
||||
import { fixURL } from '../../utils/helpers/github/url'
|
||||
import { tryFocus } from '../../utils/helpers/shared'
|
||||
import { getCardBackgroundThemeColor } from '../columns/ColumnRenderer'
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { ReactElement, ReactNode } from 'react'
|
||||
|
||||
import { CardViewMode } from '@devhub/core'
|
||||
import { CardViewMode, mergeMaxLength } from '@devhub/core'
|
||||
import { ScrollView, View } from 'react-native'
|
||||
import { mergeMaxLength } from '../../../../utils/helpers/filters'
|
||||
import { topCardMargin } from './styles'
|
||||
|
||||
export type RenderItem<T> = (params: {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
import { GitHubNotificationReason } from '@devhub/core'
|
||||
import {
|
||||
getNotificationReasonMetadata,
|
||||
GitHubNotificationReason,
|
||||
} from '@devhub/core'
|
||||
import { Platform } from '../../../../../libs/platform'
|
||||
import { mutedOpacity, smallerTextSize } from '../../../../../styles/variables'
|
||||
import { getNotificationReasonMetadata } from '../../../../../utils/helpers/github/notifications'
|
||||
import { ThemedText } from '../../../../themed/ThemedText'
|
||||
|
||||
export interface NotificationReasonProps {
|
||||
|
||||
@@ -5,11 +5,19 @@ import { ScrollView, View } from 'react-native'
|
||||
import {
|
||||
Column,
|
||||
eventActions,
|
||||
eventSubjectTypes,
|
||||
filterRecordHasAnyForcedValue,
|
||||
filterRecordWithThisValueCount,
|
||||
getEventActionMetadata,
|
||||
getFilterCountMetadata,
|
||||
getNotificationReasonMetadata,
|
||||
GitHubEventSubjectType,
|
||||
GitHubNotificationSubjectType,
|
||||
isReadFilterChecked,
|
||||
issueOrPullRequestSubjectTypes,
|
||||
isUnreadFilterChecked,
|
||||
notificationReasons,
|
||||
notificationSubjectTypes,
|
||||
ThemeColors,
|
||||
} from '@devhub/core'
|
||||
import { useAppViewMode } from '../../hooks/use-app-view-mode'
|
||||
@@ -23,18 +31,6 @@ import {
|
||||
columnHeaderItemContentSize,
|
||||
contentPadding,
|
||||
} from '../../styles/variables'
|
||||
import {
|
||||
filterRecordHasAnyForcedValue,
|
||||
filterRecordWithThisValueCount,
|
||||
getFilterCountMetadata,
|
||||
} from '../../utils/helpers/filters'
|
||||
import { eventSubjectTypes } from '../../utils/helpers/github/events'
|
||||
import { issueOrPullRequestSubjectTypes } from '../../utils/helpers/github/issues'
|
||||
import {
|
||||
getNotificationReasonMetadata,
|
||||
notificationReasons,
|
||||
notificationSubjectTypes,
|
||||
} from '../../utils/helpers/github/notifications'
|
||||
import { getSubjectTypeMetadata } from '../../utils/helpers/github/shared'
|
||||
import { CardItemSeparator } from '../cards/partials/CardItemSeparator'
|
||||
import { Checkbox } from '../common/Checkbox'
|
||||
@@ -82,10 +78,10 @@ export interface ColumnOptionsProps {
|
||||
export type ColumnOptionCategory =
|
||||
| 'event_action'
|
||||
| 'inbox'
|
||||
| 'subject_types'
|
||||
| 'notification_reason'
|
||||
| 'privacy'
|
||||
| 'saved_for_later'
|
||||
| 'subject_types'
|
||||
| 'unread'
|
||||
|
||||
export const ColumnOptions = React.memo((props: ColumnOptionsProps) => {
|
||||
@@ -102,8 +98,8 @@ export const ColumnOptions = React.memo((props: ColumnOptionsProps) => {
|
||||
column.type === 'notifications' && 'inbox',
|
||||
'saved_for_later',
|
||||
'unread',
|
||||
column.type === 'activity' && 'event_action',
|
||||
'subject_types',
|
||||
column.type === 'activity' && 'event_action',
|
||||
column.type === 'notifications' && 'notification_reason',
|
||||
column.type === 'notifications' && 'privacy',
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Dimensions, View } from 'react-native'
|
||||
|
||||
import {
|
||||
activityColumnHasAnyFilter,
|
||||
CardViewMode,
|
||||
Column as ColumnType,
|
||||
constants,
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
isEventPrivate,
|
||||
isItemRead,
|
||||
isNotificationPrivate,
|
||||
notificationColumnHasAnyFilter,
|
||||
ThemeColors,
|
||||
} from '@devhub/core'
|
||||
import { useAppViewMode } from '../../hooks/use-app-view-mode'
|
||||
@@ -29,10 +31,6 @@ import {
|
||||
contentPadding,
|
||||
sidebarSize,
|
||||
} from '../../styles/variables'
|
||||
import {
|
||||
activityColumnHasAnyFilter,
|
||||
notificationColumnHasAnyFilter,
|
||||
} from '../../utils/helpers/filters'
|
||||
import { FreeTrialHeaderMessage } from '../common/FreeTrialHeaderMessage'
|
||||
import { separatorSize, separatorThickSize } from '../common/Separator'
|
||||
import { Spacer } from '../common/Spacer'
|
||||
|
||||
@@ -4,13 +4,13 @@ import {
|
||||
ActivityColumn,
|
||||
Column,
|
||||
ColumnSubscription,
|
||||
filterRecordHasAnyForcedValue,
|
||||
getEventMetadata,
|
||||
GitHubEvent,
|
||||
GraphQLGitHubUser,
|
||||
guid,
|
||||
removeUselessURLsFromResponseItem,
|
||||
} from '@devhub/core'
|
||||
import { filterRecordHasAnyForcedValue } from '../utils/helpers/filters'
|
||||
import * as selectors from './selectors'
|
||||
import { RootState } from './types'
|
||||
|
||||
|
||||
@@ -4,14 +4,14 @@ import _ from 'lodash'
|
||||
import {
|
||||
ColumnSubscription,
|
||||
constants,
|
||||
mergeEventsPreservingEnhancement,
|
||||
mergeNotificationsPreservingEnhancement,
|
||||
normalizeSubscriptions,
|
||||
removeUselessURLsFromResponseItem,
|
||||
sortEvents,
|
||||
sortIssuesOrPullRequests,
|
||||
sortNotifications,
|
||||
} from '@devhub/core'
|
||||
import { mergeEventsPreservingEnhancement } from '../../utils/helpers/github/events'
|
||||
import { mergeNotificationsPreservingEnhancement } from '../../utils/helpers/github/notifications'
|
||||
import { Reducer } from '../types'
|
||||
|
||||
export interface State {
|
||||
|
||||
@@ -12,11 +12,11 @@ import {
|
||||
isReadFilterChecked,
|
||||
IssueOrPullRequestColumn,
|
||||
IssueOrPullRequestColumnSubscription,
|
||||
itemPassesFilterRecord,
|
||||
NotificationColumn,
|
||||
NotificationColumnSubscription,
|
||||
} from '@devhub/core'
|
||||
import { emitter } from '../../libs/emitter'
|
||||
import { itemPassesFilterRecord } from '../../utils/helpers/filters'
|
||||
import * as actions from '../actions'
|
||||
import * as selectors from '../selectors'
|
||||
import { ExtractActionFromActionCreator } from '../types/base'
|
||||
|
||||
@@ -35,6 +35,9 @@ import {
|
||||
GitHubIssueOrPullRequest,
|
||||
GitHubNotification,
|
||||
IssueOrPullRequestColumnSubscription,
|
||||
mergeEventsPreservingEnhancement,
|
||||
mergeIssuesOrPullRequestsPreservingEnhancement,
|
||||
mergeNotificationsPreservingEnhancement,
|
||||
} from '@devhub/core'
|
||||
|
||||
import { bugsnag } from '../../libs/bugsnag'
|
||||
@@ -44,9 +47,6 @@ import {
|
||||
getNotifications,
|
||||
octokit,
|
||||
} from '../../libs/github'
|
||||
import { mergeEventsPreservingEnhancement } from '../../utils/helpers/github/events'
|
||||
import { mergeIssuesOrPullRequestsPreservingEnhancement } from '../../utils/helpers/github/issues'
|
||||
import { mergeNotificationsPreservingEnhancement } from '../../utils/helpers/github/notifications'
|
||||
import * as actions from '../actions'
|
||||
import * as selectors from '../selectors'
|
||||
import { ExtractActionFromActionCreator } from '../types/base'
|
||||
|
||||
@@ -6,15 +6,13 @@ import {
|
||||
EnhancedGitHubEvent,
|
||||
EnhancedGitHubIssueOrPullRequest,
|
||||
EnhancedGitHubNotification,
|
||||
getFilteredEvents,
|
||||
getFilteredIssueOrPullRequests,
|
||||
getFilteredNotifications,
|
||||
sortEvents,
|
||||
sortIssuesOrPullRequests,
|
||||
sortNotifications,
|
||||
} from '@devhub/core'
|
||||
import {
|
||||
getFilteredEvents,
|
||||
getFilteredIssueOrPullRequests,
|
||||
getFilteredNotifications,
|
||||
} from '../../utils/helpers/filters'
|
||||
import { RootState } from '../types'
|
||||
import { createArraySelector } from './helpers'
|
||||
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
|
||||
import {
|
||||
ActivityColumnFilters,
|
||||
BaseColumnFilters,
|
||||
EnhancedGitHubEvent,
|
||||
EnhancedGitHubIssueOrPullRequest,
|
||||
EnhancedGitHubNotification,
|
||||
getEventMetadata,
|
||||
getIssueOrPullRequestSubjectType,
|
||||
isEventPrivate,
|
||||
isItemRead,
|
||||
isNotificationPrivate,
|
||||
IssueOrPullRequestColumnFilters,
|
||||
mergeSimilarEvents,
|
||||
NotificationColumnFilters,
|
||||
sortEvents,
|
||||
sortIssuesOrPullRequests,
|
||||
sortNotifications,
|
||||
} from '@devhub/core'
|
||||
import { getEventSubjectType } from './github/events'
|
||||
import { getNotificationSubjectType } from './github/notifications'
|
||||
|
||||
export const filterRecordHasAnyForcedValue = (
|
||||
filtersRecord: Record<string, boolean | undefined> | undefined,
|
||||
) => {
|
||||
if (!filtersRecord) return false
|
||||
return Object.values(filtersRecord).some(value => typeof value === 'boolean')
|
||||
}
|
||||
|
||||
export const filterRecordWithThisValueCount = (
|
||||
filtersRecord: Record<string, boolean | undefined> | undefined,
|
||||
valueToCheck: boolean,
|
||||
): number => {
|
||||
if (!filtersRecord) return 0
|
||||
|
||||
return Object.values(filtersRecord).reduce(
|
||||
(total, item) => total + (item === valueToCheck ? 1 : 0),
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
export function itemPassesFilterRecord<
|
||||
F extends Record<string, boolean | undefined>
|
||||
>(filtersRecord: F, value: keyof F, defaultValue: boolean) {
|
||||
if (!(filtersRecord && value)) return defaultValue
|
||||
|
||||
const hasForcedFilter = filterRecordHasAnyForcedValue(filtersRecord)
|
||||
if (!hasForcedFilter) return defaultValue
|
||||
|
||||
const isFilterStrict =
|
||||
hasForcedFilter &&
|
||||
filterRecordWithThisValueCount(filtersRecord, defaultValue)
|
||||
|
||||
return filtersRecord[value] === !defaultValue ||
|
||||
(filtersRecord[value] !== defaultValue && isFilterStrict)
|
||||
? !defaultValue
|
||||
: defaultValue
|
||||
}
|
||||
|
||||
export function getFilterCountMetadata(
|
||||
filtersRecord: Record<string, boolean | undefined> | undefined,
|
||||
totalCount: number,
|
||||
defaultValue: boolean,
|
||||
): { checked: number; unchecked: number; total: number } {
|
||||
if (!filtersRecord) return { checked: 0, unchecked: 0, total: totalCount }
|
||||
|
||||
const keys = Object.keys(filtersRecord)
|
||||
|
||||
const hasForcedFilter = filterRecordHasAnyForcedValue(filtersRecord)
|
||||
if (!hasForcedFilter) {
|
||||
return {
|
||||
checked: defaultValue ? totalCount : 0,
|
||||
unchecked: !defaultValue ? totalCount : 0,
|
||||
total: totalCount,
|
||||
}
|
||||
}
|
||||
|
||||
const isFilterStrict =
|
||||
hasForcedFilter &&
|
||||
filterRecordWithThisValueCount(filtersRecord, defaultValue)
|
||||
|
||||
if (isFilterStrict) {
|
||||
return keys.reduce(
|
||||
(result, key) => {
|
||||
const checked = filtersRecord[key] === defaultValue
|
||||
|
||||
return {
|
||||
...result,
|
||||
checked: checked ? result.checked + 1 : result.checked,
|
||||
unchecked: !checked ? result.unchecked + 1 : result.unchecked,
|
||||
}
|
||||
},
|
||||
{ checked: 0, unchecked: 0, total: totalCount },
|
||||
)
|
||||
}
|
||||
|
||||
return keys.reduce(
|
||||
(result, key) => {
|
||||
const checked =
|
||||
filtersRecord[key] === !defaultValue ? !defaultValue : defaultValue
|
||||
|
||||
return {
|
||||
...result,
|
||||
checked: checked ? result.checked : result.checked - 1,
|
||||
unchecked: !checked ? result.unchecked : result.unchecked - 1,
|
||||
}
|
||||
},
|
||||
{ checked: totalCount, unchecked: totalCount, total: totalCount },
|
||||
)
|
||||
}
|
||||
|
||||
function baseColumnHasAnyFilter(filters: BaseColumnFilters | undefined) {
|
||||
if (!filters) return false
|
||||
|
||||
if (filters.clearedAt) return true
|
||||
if (typeof filters.private === 'boolean') return true
|
||||
if (typeof filters.saved === 'boolean') return true
|
||||
|
||||
if (
|
||||
filters.subjectTypes &&
|
||||
filterRecordHasAnyForcedValue(filters.subjectTypes)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (typeof filters.unread === 'boolean') return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function activityColumnHasAnyFilter(
|
||||
filters: ActivityColumnFilters | undefined,
|
||||
) {
|
||||
if (!filters) return false
|
||||
|
||||
if (baseColumnHasAnyFilter(filters)) return true
|
||||
|
||||
if (
|
||||
filters.activity &&
|
||||
filterRecordHasAnyForcedValue(filters.activity.actions)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function issueOrPullRequestColumnHasAnyFilter(
|
||||
filters: IssueOrPullRequestColumnFilters | undefined,
|
||||
) {
|
||||
if (!filters) return false
|
||||
|
||||
if (baseColumnHasAnyFilter(filters)) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function notificationColumnHasAnyFilter(
|
||||
filters: NotificationColumnFilters | undefined,
|
||||
) {
|
||||
if (!filters) return false
|
||||
|
||||
if (baseColumnHasAnyFilter(filters)) return true
|
||||
|
||||
if (filters.notifications && filters.notifications.participating) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
filters.notifications &&
|
||||
filterRecordHasAnyForcedValue(filters.notifications.reasons)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function getFilteredIssueOrPullRequests(
|
||||
items: EnhancedGitHubIssueOrPullRequest[],
|
||||
filters: IssueOrPullRequestColumnFilters | undefined,
|
||||
) {
|
||||
let _items = sortIssuesOrPullRequests(items)
|
||||
|
||||
if (filters && issueOrPullRequestColumnHasAnyFilter(filters)) {
|
||||
_items = _items.filter(item => {
|
||||
if (
|
||||
!itemPassesFilterRecord(
|
||||
filters.subjectTypes!,
|
||||
getIssueOrPullRequestSubjectType(item)!,
|
||||
true,
|
||||
)
|
||||
)
|
||||
return false
|
||||
|
||||
if (
|
||||
typeof filters.unread === 'boolean' &&
|
||||
filters.unread !== !isItemRead(item)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
const showSaveForLater = filters.saved !== false
|
||||
const showInbox = filters.saved !== true
|
||||
const showCleared = false
|
||||
|
||||
if (
|
||||
filters.clearedAt &&
|
||||
(!item.updated_at || item.updated_at <= filters.clearedAt)
|
||||
)
|
||||
if (!(showSaveForLater && item.saved))
|
||||
/* && isItemRead(notification) */
|
||||
return showCleared
|
||||
|
||||
if (item.saved) return showSaveForLater
|
||||
|
||||
return showInbox
|
||||
})
|
||||
}
|
||||
|
||||
return _items
|
||||
}
|
||||
|
||||
export function getFilteredNotifications(
|
||||
notifications: EnhancedGitHubNotification[],
|
||||
filters: NotificationColumnFilters | undefined,
|
||||
) {
|
||||
let _notifications = sortNotifications(notifications)
|
||||
|
||||
const reasonsFilter =
|
||||
filters && filters.notifications && filters.notifications.reasons
|
||||
|
||||
if (filters && notificationColumnHasAnyFilter(filters)) {
|
||||
_notifications = _notifications.filter(notification => {
|
||||
if (!itemPassesFilterRecord(reasonsFilter!, notification.reason, true))
|
||||
return false
|
||||
|
||||
if (
|
||||
filters.notifications &&
|
||||
filters.notifications.participating &&
|
||||
notification.reason === 'subscribed'
|
||||
)
|
||||
return false
|
||||
|
||||
if (
|
||||
!itemPassesFilterRecord(
|
||||
filters.subjectTypes!,
|
||||
getNotificationSubjectType(notification)!,
|
||||
true,
|
||||
)
|
||||
)
|
||||
return false
|
||||
|
||||
if (
|
||||
typeof filters.unread === 'boolean' &&
|
||||
filters.unread !== !isItemRead(notification)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
typeof filters.private === 'boolean' &&
|
||||
isNotificationPrivate(notification) !== filters.private
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
const showSaveForLater = filters.saved !== false
|
||||
const showInbox = filters.saved !== true
|
||||
const showCleared = false
|
||||
|
||||
if (
|
||||
filters.clearedAt &&
|
||||
(!notification.updated_at ||
|
||||
notification.updated_at <= filters.clearedAt)
|
||||
)
|
||||
if (!(showSaveForLater && notification.saved))
|
||||
/* && isItemRead(notification) */
|
||||
return showCleared
|
||||
|
||||
if (notification.saved) return showSaveForLater
|
||||
|
||||
return showInbox
|
||||
})
|
||||
}
|
||||
|
||||
return _notifications
|
||||
}
|
||||
|
||||
export function getFilteredEvents(
|
||||
events: EnhancedGitHubEvent[],
|
||||
filters: ActivityColumnFilters | undefined,
|
||||
mergeSimilar: boolean,
|
||||
) {
|
||||
let _events = sortEvents(events)
|
||||
|
||||
const actionFilter = filters && filters.activity && filters.activity.actions
|
||||
|
||||
if (filters && activityColumnHasAnyFilter(filters)) {
|
||||
_events = _events.filter(event => {
|
||||
if (
|
||||
!itemPassesFilterRecord(
|
||||
actionFilter!,
|
||||
getEventMetadata(event).action!,
|
||||
true,
|
||||
)
|
||||
)
|
||||
return false
|
||||
|
||||
if (
|
||||
!itemPassesFilterRecord(
|
||||
filters.subjectTypes!,
|
||||
getEventSubjectType(event)!,
|
||||
true,
|
||||
)
|
||||
)
|
||||
return false
|
||||
|
||||
if (
|
||||
typeof filters.unread === 'boolean' &&
|
||||
filters.unread !== !isItemRead(event)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
typeof filters.private === 'boolean' &&
|
||||
isEventPrivate(event) !== filters.private
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
const showSaveForLater = filters.saved !== false
|
||||
const showInbox = filters.saved !== true
|
||||
const showCleared = false
|
||||
|
||||
if (
|
||||
filters.clearedAt &&
|
||||
(!event.created_at || event.created_at <= filters.clearedAt)
|
||||
)
|
||||
if (!(showSaveForLater && event.saved))
|
||||
/* && isItemRead(event) */
|
||||
return showCleared
|
||||
|
||||
if (event.saved) return showSaveForLater
|
||||
|
||||
return showInbox
|
||||
})
|
||||
}
|
||||
|
||||
return mergeSimilar ? mergeSimilarEvents(_events, mergeMaxLength) : _events
|
||||
}
|
||||
|
||||
export const mergeMaxLength = 5
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
|
||||
import {
|
||||
EnhancedGitHubEvent,
|
||||
GitHubEventSubjectType,
|
||||
GitHubIcon,
|
||||
GitHubIssue,
|
||||
GitHubPullRequest,
|
||||
isPullRequest,
|
||||
isTagMainEvent,
|
||||
sortEvents,
|
||||
ThemeColors,
|
||||
} from '@devhub/core'
|
||||
import { bugsnag } from '../../../libs/bugsnag'
|
||||
import {
|
||||
getCommitIconAndColor,
|
||||
getIssueIconAndColor,
|
||||
getPullRequestIconAndColor,
|
||||
getReleaseIconAndColor,
|
||||
getTagIconAndColor,
|
||||
} from './shared'
|
||||
|
||||
export const eventSubjectTypes: GitHubEventSubjectType[] = [
|
||||
'Branch',
|
||||
'Commit',
|
||||
'Issue',
|
||||
'PullRequest',
|
||||
'PullRequestReview',
|
||||
'Release',
|
||||
'Repository',
|
||||
'Tag',
|
||||
'User',
|
||||
'Wiki',
|
||||
]
|
||||
|
||||
export function getEventSubjectType(
|
||||
event: EnhancedGitHubEvent,
|
||||
): GitHubEventSubjectType | null {
|
||||
if (!(event && event.type)) return null
|
||||
|
||||
switch (event.type) {
|
||||
case 'CommitCommentEvent':
|
||||
return 'Commit'
|
||||
|
||||
case 'CreateEvent':
|
||||
case 'DeleteEvent': {
|
||||
switch (event.payload.ref_type) {
|
||||
case 'repository':
|
||||
return 'Repository'
|
||||
case 'branch':
|
||||
return 'Branch'
|
||||
case 'tag':
|
||||
return 'Tag'
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
case 'ForkEvent':
|
||||
return 'Repository'
|
||||
case 'GollumEvent':
|
||||
return 'Wiki'
|
||||
case 'IssueCommentEvent':
|
||||
return 'Issue'
|
||||
case 'IssuesEvent':
|
||||
return 'Issue'
|
||||
case 'MemberEvent':
|
||||
return 'User'
|
||||
case 'PublicEvent':
|
||||
return 'Repository'
|
||||
case 'PullRequestEvent':
|
||||
return 'PullRequest'
|
||||
case 'PullRequestReviewCommentEvent':
|
||||
return 'PullRequestReview'
|
||||
case 'PullRequestReviewEvent':
|
||||
return 'PullRequestReview'
|
||||
case 'PushEvent':
|
||||
return 'Commit'
|
||||
case 'ReleaseEvent':
|
||||
return 'Release'
|
||||
case 'WatchEvent':
|
||||
case 'WatchEvent:OneUserMultipleRepos':
|
||||
return 'Repository'
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function getEventIconAndColor(
|
||||
event: EnhancedGitHubEvent,
|
||||
): { color?: keyof ThemeColors; icon: GitHubIcon; subIcon?: GitHubIcon } {
|
||||
switch (event.type) {
|
||||
case 'CommitCommentEvent':
|
||||
return {
|
||||
...getCommitIconAndColor(),
|
||||
subIcon: 'comment-discussion',
|
||||
}
|
||||
|
||||
case 'CreateEvent': {
|
||||
switch (event.payload.ref_type) {
|
||||
case 'repository':
|
||||
return { icon: 'repo' }
|
||||
case 'branch':
|
||||
return { icon: 'git-branch' }
|
||||
case 'tag':
|
||||
return { icon: 'tag' }
|
||||
default:
|
||||
return { icon: 'plus' }
|
||||
}
|
||||
}
|
||||
|
||||
case 'DeleteEvent': {
|
||||
switch (event.payload.ref_type) {
|
||||
case 'repository':
|
||||
return { icon: 'repo', color: 'red' }
|
||||
case 'branch':
|
||||
return { icon: 'git-branch', color: 'red' }
|
||||
case 'tag':
|
||||
return { icon: 'tag', color: 'red' }
|
||||
default:
|
||||
return { icon: 'trashcan' }
|
||||
}
|
||||
}
|
||||
|
||||
case 'ForkEvent':
|
||||
return { icon: 'repo-forked' }
|
||||
|
||||
case 'GollumEvent':
|
||||
return { icon: 'book' }
|
||||
|
||||
case 'IssueCommentEvent': {
|
||||
return {
|
||||
...(isPullRequest(event.payload.issue)
|
||||
? getPullRequestIconAndColor(event.payload.issue as GitHubPullRequest)
|
||||
: getIssueIconAndColor(event.payload.issue)),
|
||||
subIcon: 'comment-discussion',
|
||||
}
|
||||
}
|
||||
|
||||
case 'IssuesEvent': {
|
||||
const issue = event.payload.issue
|
||||
|
||||
switch (event.payload.action) {
|
||||
case 'opened':
|
||||
return getIssueIconAndColor({ state: 'open' } as GitHubIssue)
|
||||
case 'closed':
|
||||
return getIssueIconAndColor({ state: 'closed' } as GitHubIssue)
|
||||
|
||||
case 'reopened':
|
||||
return {
|
||||
...getIssueIconAndColor({ state: 'open' } as GitHubIssue),
|
||||
icon: 'issue-reopened',
|
||||
}
|
||||
// case 'assigned':
|
||||
// case 'unassigned':
|
||||
// case 'labeled':
|
||||
// case 'unlabeled':
|
||||
// case 'edited':
|
||||
// case 'milestoned':
|
||||
// case 'demilestoned':
|
||||
default:
|
||||
return getIssueIconAndColor(issue)
|
||||
}
|
||||
}
|
||||
case 'MemberEvent':
|
||||
return { icon: 'person' }
|
||||
|
||||
case 'PublicEvent':
|
||||
return { icon: 'globe', color: 'blue' }
|
||||
|
||||
case 'PullRequestEvent': {
|
||||
const pullRequest = event.payload.pull_request
|
||||
|
||||
switch (event.payload.action) {
|
||||
case 'opened':
|
||||
case 'reopened':
|
||||
return getPullRequestIconAndColor({
|
||||
draft: pullRequest.draft,
|
||||
state: 'open',
|
||||
merged: false,
|
||||
merged_at: undefined,
|
||||
mergeable_state: pullRequest.mergeable_state,
|
||||
})
|
||||
// case 'closed': return getPullRequestIconAndColor({ state: 'closed' } as GitHubPullRequest);
|
||||
|
||||
// case 'assigned':
|
||||
// case 'unassigned':
|
||||
// case 'labeled':
|
||||
// case 'unlabeled':
|
||||
// case 'edited':
|
||||
default:
|
||||
return getPullRequestIconAndColor(pullRequest)
|
||||
}
|
||||
}
|
||||
|
||||
case 'PullRequestReviewCommentEvent':
|
||||
case 'PullRequestReviewEvent': {
|
||||
return {
|
||||
...getPullRequestIconAndColor(event.payload.pull_request),
|
||||
subIcon: 'comment-discussion',
|
||||
}
|
||||
}
|
||||
|
||||
case 'PushEvent':
|
||||
return { icon: 'code' }
|
||||
|
||||
case 'ReleaseEvent':
|
||||
return isTagMainEvent(event)
|
||||
? getTagIconAndColor()
|
||||
: getReleaseIconAndColor()
|
||||
|
||||
case 'WatchEvent':
|
||||
case 'WatchEvent:OneUserMultipleRepos':
|
||||
return { icon: 'star', color: 'yellow' }
|
||||
|
||||
default: {
|
||||
const message = `Unknown event type: ${(event as any).type}`
|
||||
bugsnag.notify(new Error(message))
|
||||
console.error(message)
|
||||
return { icon: 'mark-github' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeEventsPreservingEnhancement(
|
||||
newItems: EnhancedGitHubEvent[],
|
||||
prevItems: EnhancedGitHubEvent[],
|
||||
) {
|
||||
return sortEvents(
|
||||
_.uniqBy(_.concat(newItems, prevItems), 'id').map(item => {
|
||||
const newItem = newItems.find(i => i.id === item.id)
|
||||
const existingItem = prevItems.find(i => i.id === item.id)
|
||||
if (!(newItem && existingItem)) return item
|
||||
|
||||
const mergedItem = {
|
||||
forceUnreadLocally: existingItem.forceUnreadLocally,
|
||||
last_read_at: existingItem.last_read_at,
|
||||
last_unread_at: existingItem.last_unread_at,
|
||||
saved: existingItem.saved,
|
||||
unread: existingItem.unread,
|
||||
...newItem,
|
||||
}
|
||||
|
||||
return _.isEqual(mergedItem, existingItem) ? existingItem : mergedItem
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
|
||||
import {
|
||||
EnhancedGitHubIssueOrPullRequest,
|
||||
GitHubIssue,
|
||||
GitHubIssueOrPullRequest,
|
||||
GitHubIssueOrPullRequestSubjectType,
|
||||
GitHubPullRequest,
|
||||
sortIssuesOrPullRequests,
|
||||
} from '@devhub/core'
|
||||
import { getIssueIconAndColor, getPullRequestIconAndColor } from './shared'
|
||||
|
||||
export const issueOrPullRequestSubjectTypes: GitHubIssueOrPullRequestSubjectType[] = [
|
||||
'Issue',
|
||||
'PullRequest',
|
||||
]
|
||||
|
||||
export function getIssueOrPullRequestIconAndColor(
|
||||
type: GitHubIssueOrPullRequestSubjectType,
|
||||
issueOrPullRequest: GitHubIssueOrPullRequest,
|
||||
) {
|
||||
return type === 'PullRequest'
|
||||
? getPullRequestIconAndColor(issueOrPullRequest as GitHubPullRequest)
|
||||
: getIssueIconAndColor(issueOrPullRequest as GitHubIssue)
|
||||
}
|
||||
|
||||
export function mergeIssuesOrPullRequestsPreservingEnhancement(
|
||||
newItems: EnhancedGitHubIssueOrPullRequest[],
|
||||
prevItems: EnhancedGitHubIssueOrPullRequest[],
|
||||
) {
|
||||
return sortIssuesOrPullRequests(
|
||||
_.uniqBy(_.concat(newItems || [], prevItems || []), 'id').map(item => {
|
||||
const newItem = (newItems || []).find(i => i.id === item.id)
|
||||
const existingItem = prevItems.find(i => i.id === item.id)
|
||||
if (!(newItem && existingItem)) return item
|
||||
|
||||
const mergedItem = {
|
||||
forceUnreadLocally: existingItem.forceUnreadLocally,
|
||||
last_read_at: existingItem.last_read_at,
|
||||
last_unread_at: existingItem.last_unread_at,
|
||||
saved: existingItem.saved,
|
||||
unread: existingItem.unread,
|
||||
...newItem,
|
||||
}
|
||||
|
||||
return _.isEqual(mergedItem, existingItem) ? existingItem : mergedItem
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
|
||||
import {
|
||||
capitalize,
|
||||
EnhancedGitHubNotification,
|
||||
GitHubIcon,
|
||||
GitHubIssue,
|
||||
GitHubIssueOrPullRequest,
|
||||
GitHubNotification,
|
||||
GitHubNotificationReason,
|
||||
GitHubNotificationSubjectType,
|
||||
GitHubPullRequest,
|
||||
sortNotifications,
|
||||
ThemeColors,
|
||||
} from '@devhub/core'
|
||||
import { bugsnag } from '../../../libs/bugsnag'
|
||||
import {
|
||||
getCommitIconAndColor,
|
||||
getIssueIconAndColor,
|
||||
getPullRequestIconAndColor,
|
||||
getReleaseIconAndColor,
|
||||
} from './shared'
|
||||
|
||||
export const notificationReasons: GitHubNotificationReason[] = [
|
||||
'assign',
|
||||
'author',
|
||||
'comment',
|
||||
'invitation',
|
||||
'manual',
|
||||
'mention',
|
||||
'review_requested',
|
||||
'security_alert',
|
||||
'state_change',
|
||||
'subscribed',
|
||||
'team_mention',
|
||||
]
|
||||
|
||||
export const notificationSubjectTypes: GitHubNotificationSubjectType[] = [
|
||||
'Commit',
|
||||
'Issue',
|
||||
'PullRequest',
|
||||
'Release',
|
||||
'RepositoryInvitation',
|
||||
'RepositoryVulnerabilityAlert',
|
||||
]
|
||||
|
||||
export function getNotificationSubjectType(
|
||||
notification: GitHubNotification,
|
||||
): GitHubNotificationSubjectType | null {
|
||||
if (!(notification && notification.subject && notification.subject.type))
|
||||
return null
|
||||
|
||||
return notification.subject.type
|
||||
}
|
||||
|
||||
export function getNotificationIconAndColor(
|
||||
notification: GitHubNotification,
|
||||
payload: GitHubIssueOrPullRequest | undefined,
|
||||
): { icon: GitHubIcon; color?: keyof ThemeColors; tooltip: string } {
|
||||
const { subject } = notification
|
||||
const { type } = subject
|
||||
|
||||
switch (type) {
|
||||
case 'Commit':
|
||||
return getCommitIconAndColor()
|
||||
case 'Issue':
|
||||
return getIssueIconAndColor(payload as GitHubIssue)
|
||||
case 'PullRequest':
|
||||
return getPullRequestIconAndColor(payload as GitHubPullRequest)
|
||||
case 'Release':
|
||||
return getReleaseIconAndColor()
|
||||
case 'RepositoryInvitation':
|
||||
return {
|
||||
icon: 'mail',
|
||||
color: 'brown',
|
||||
tooltip: 'Repository invitation',
|
||||
}
|
||||
case 'RepositoryVulnerabilityAlert':
|
||||
return {
|
||||
icon: 'alert',
|
||||
color: 'yellow',
|
||||
tooltip: 'Repository vulnerability alert',
|
||||
}
|
||||
default: {
|
||||
const message = `Unknown event type: ${(event as any).type}`
|
||||
bugsnag.notify(new Error(message))
|
||||
console.error(message)
|
||||
return { icon: 'bell', tooltip: '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getNotificationReasonMetadata<
|
||||
T extends GitHubNotificationReason
|
||||
>(
|
||||
reason: T,
|
||||
): {
|
||||
color: keyof ThemeColors
|
||||
reason: T
|
||||
label: string
|
||||
fullDescription: string
|
||||
// smallDescription: string
|
||||
} {
|
||||
switch (reason) {
|
||||
case 'assign':
|
||||
return {
|
||||
reason,
|
||||
color: 'pink',
|
||||
fullDescription: 'You were assigned to the thread',
|
||||
// smallDescription: 'You were assigned',
|
||||
label: 'Assigned',
|
||||
}
|
||||
|
||||
case 'author':
|
||||
return {
|
||||
reason,
|
||||
color: 'lightRed',
|
||||
fullDescription: 'You created the thread',
|
||||
// smallDescription: 'You created',
|
||||
label: 'Author',
|
||||
}
|
||||
|
||||
case 'comment':
|
||||
return {
|
||||
reason,
|
||||
color: 'blue',
|
||||
fullDescription: 'You commented on the thread',
|
||||
// smallDescription: 'You commented',
|
||||
label: 'Commented',
|
||||
}
|
||||
|
||||
case 'invitation':
|
||||
return {
|
||||
reason,
|
||||
color: 'brown',
|
||||
fullDescription:
|
||||
'You accepted an invitation to contribute to the repository',
|
||||
// smallDescription: 'You were invited',
|
||||
label: 'Invited',
|
||||
}
|
||||
|
||||
case 'manual':
|
||||
return {
|
||||
reason,
|
||||
color: 'teal',
|
||||
fullDescription: 'You manually subscribed to the thread',
|
||||
// smallDescription: 'You subscribed manually',
|
||||
label: 'Manual',
|
||||
}
|
||||
|
||||
case 'mention':
|
||||
return {
|
||||
reason,
|
||||
color: 'orange',
|
||||
fullDescription: 'You were @mentioned in the thread',
|
||||
// smallDescription: 'You were mentioned',
|
||||
label: 'Mentioned',
|
||||
}
|
||||
|
||||
case 'state_change':
|
||||
return {
|
||||
reason,
|
||||
color: 'purple',
|
||||
fullDescription: 'You opened or closed the issue/pr',
|
||||
// smallDescription: 'You opened/closed',
|
||||
label: 'State changed',
|
||||
}
|
||||
|
||||
case 'subscribed':
|
||||
return {
|
||||
reason,
|
||||
color: 'blueGray',
|
||||
fullDescription: "You're watching the repository",
|
||||
// smallDescription: 'You are watching',
|
||||
label: 'Watching',
|
||||
}
|
||||
|
||||
case 'team_mention':
|
||||
return {
|
||||
reason,
|
||||
color: 'yellow',
|
||||
fullDescription: 'Your team was mentioned in the thread',
|
||||
// smallDescription: 'Team mentioned',
|
||||
label: 'Team mentioned',
|
||||
}
|
||||
|
||||
case 'review_requested':
|
||||
return {
|
||||
reason,
|
||||
color: 'yellow',
|
||||
fullDescription: 'Someone requested your review in the pull request',
|
||||
// smallDescription: 'Review requested',
|
||||
label: 'Review requested',
|
||||
}
|
||||
|
||||
case 'security_alert':
|
||||
return {
|
||||
reason,
|
||||
color: 'red',
|
||||
fullDescription: 'Potential security vulnerability found',
|
||||
// smallDescription: 'Security alert',
|
||||
label: 'Security',
|
||||
}
|
||||
|
||||
default:
|
||||
return {
|
||||
reason,
|
||||
color: 'gray',
|
||||
fullDescription: '',
|
||||
// smallDescription: '',
|
||||
label: capitalize(reason),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeNotificationsPreservingEnhancement(
|
||||
newItems: EnhancedGitHubNotification[],
|
||||
prevItems: EnhancedGitHubNotification[],
|
||||
) {
|
||||
return sortNotifications(
|
||||
_.uniqBy(_.concat(newItems, prevItems), 'id').map(item => {
|
||||
const newItem = newItems.find(i => i.id === item.id)
|
||||
const existingItem = prevItems.find(i => i.id === item.id)
|
||||
if (!(newItem && existingItem)) return item
|
||||
|
||||
const mergedItem = {
|
||||
forceUnreadLocally: existingItem.forceUnreadLocally,
|
||||
last_read_at: existingItem.last_read_at,
|
||||
last_unread_at: existingItem.last_unread_at,
|
||||
saved: existingItem.saved,
|
||||
unread: existingItem.unread,
|
||||
...newItem,
|
||||
}
|
||||
|
||||
return _.isEqual(mergedItem, existingItem) ? existingItem : mergedItem
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -122,7 +122,9 @@ export function getSubjectTypeMetadata<
|
||||
label: string
|
||||
subjectType: T
|
||||
} {
|
||||
switch (subjectType) {
|
||||
switch (
|
||||
subjectType as GitHubEventSubjectType | GitHubNotificationSubjectType
|
||||
) {
|
||||
case 'PullRequestReview': {
|
||||
return {
|
||||
label: 'Review',
|
||||
|
||||
343
packages/core/src/helpers/filters.ts
Normal file
343
packages/core/src/helpers/filters.ts
Normal file
@@ -0,0 +1,343 @@
|
||||
import _ from 'lodash'
|
||||
import {
|
||||
ActivityColumnFilters,
|
||||
BaseColumnFilters,
|
||||
EnhancedGitHubEvent,
|
||||
EnhancedGitHubIssueOrPullRequest,
|
||||
EnhancedGitHubNotification,
|
||||
IssueOrPullRequestColumnFilters,
|
||||
NotificationColumnFilters,
|
||||
} from '../types'
|
||||
import {
|
||||
getIssueOrPullRequestSubjectType,
|
||||
getNotificationSubjectType,
|
||||
isItemRead,
|
||||
sortIssuesOrPullRequests,
|
||||
sortNotifications,
|
||||
} from './github'
|
||||
import {
|
||||
getEventMetadata,
|
||||
getEventSubjectType,
|
||||
mergeSimilarEvents,
|
||||
sortEvents,
|
||||
} from './github/events'
|
||||
import { isEventPrivate, isNotificationPrivate } from './shared'
|
||||
|
||||
export const filterRecordHasAnyForcedValue = (
|
||||
filtersRecord: Record<string, boolean | undefined> | undefined,
|
||||
) => {
|
||||
if (!filtersRecord) return false
|
||||
return Object.values(filtersRecord).some(value => typeof value === 'boolean')
|
||||
}
|
||||
|
||||
export const filterRecordWithThisValueCount = (
|
||||
filtersRecord: Record<string, boolean | undefined> | undefined,
|
||||
valueToCheck: boolean,
|
||||
): number => {
|
||||
if (!filtersRecord) return 0
|
||||
|
||||
return Object.values(filtersRecord).reduce(
|
||||
(total, item) => total + (item === valueToCheck ? 1 : 0),
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
export function itemPassesFilterRecord<
|
||||
F extends Record<string, boolean | undefined>
|
||||
>(filtersRecord: F, value: keyof F, defaultValue: boolean) {
|
||||
if (!(filtersRecord && value)) return defaultValue
|
||||
|
||||
const hasForcedFilter = filterRecordHasAnyForcedValue(filtersRecord)
|
||||
if (!hasForcedFilter) return defaultValue
|
||||
|
||||
const isFilterStrict =
|
||||
hasForcedFilter &&
|
||||
filterRecordWithThisValueCount(filtersRecord, defaultValue)
|
||||
|
||||
return filtersRecord[value] === !defaultValue ||
|
||||
(filtersRecord[value] !== defaultValue && isFilterStrict)
|
||||
? !defaultValue
|
||||
: defaultValue
|
||||
}
|
||||
|
||||
export function getFilterCountMetadata(
|
||||
filtersRecord: Record<string, boolean | undefined> | undefined,
|
||||
totalCount: number,
|
||||
defaultValue: boolean,
|
||||
): { checked: number; unchecked: number; total: number } {
|
||||
if (!filtersRecord) return { checked: 0, unchecked: 0, total: totalCount }
|
||||
|
||||
const keys = Object.keys(filtersRecord)
|
||||
|
||||
const hasForcedFilter = filterRecordHasAnyForcedValue(filtersRecord)
|
||||
if (!hasForcedFilter) {
|
||||
return {
|
||||
checked: defaultValue ? totalCount : 0,
|
||||
unchecked: !defaultValue ? totalCount : 0,
|
||||
total: totalCount,
|
||||
}
|
||||
}
|
||||
|
||||
const isFilterStrict =
|
||||
hasForcedFilter &&
|
||||
filterRecordWithThisValueCount(filtersRecord, defaultValue)
|
||||
|
||||
if (isFilterStrict) {
|
||||
return keys.reduce(
|
||||
(result, key) => {
|
||||
const checked = filtersRecord[key] === defaultValue
|
||||
|
||||
return {
|
||||
...result,
|
||||
checked: checked ? result.checked + 1 : result.checked,
|
||||
unchecked: !checked ? result.unchecked + 1 : result.unchecked,
|
||||
}
|
||||
},
|
||||
{ checked: 0, unchecked: 0, total: totalCount },
|
||||
)
|
||||
}
|
||||
|
||||
return keys.reduce(
|
||||
(result, key) => {
|
||||
const checked =
|
||||
filtersRecord[key] === !defaultValue ? !defaultValue : defaultValue
|
||||
|
||||
return {
|
||||
...result,
|
||||
checked: checked ? result.checked : result.checked - 1,
|
||||
unchecked: !checked ? result.unchecked : result.unchecked - 1,
|
||||
}
|
||||
},
|
||||
{ checked: totalCount, unchecked: totalCount, total: totalCount },
|
||||
)
|
||||
}
|
||||
|
||||
function baseColumnHasAnyFilter(filters: BaseColumnFilters | undefined) {
|
||||
if (!filters) return false
|
||||
|
||||
if (filters.clearedAt) return true
|
||||
if (typeof filters.private === 'boolean') return true
|
||||
if (typeof filters.saved === 'boolean') return true
|
||||
if (typeof filters.unread === 'boolean') return true
|
||||
|
||||
if (
|
||||
filters.subjectTypes &&
|
||||
filterRecordHasAnyForcedValue(filters.subjectTypes)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function activityColumnHasAnyFilter(
|
||||
filters: ActivityColumnFilters | undefined,
|
||||
) {
|
||||
if (!filters) return false
|
||||
|
||||
if (baseColumnHasAnyFilter(filters)) return true
|
||||
|
||||
if (
|
||||
filters.activity &&
|
||||
filterRecordHasAnyForcedValue(filters.activity.actions)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function issueOrPullRequestColumnHasAnyFilter(
|
||||
filters: IssueOrPullRequestColumnFilters | undefined,
|
||||
) {
|
||||
if (!filters) return false
|
||||
|
||||
if (baseColumnHasAnyFilter(filters)) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function notificationColumnHasAnyFilter(
|
||||
filters: NotificationColumnFilters | undefined,
|
||||
) {
|
||||
if (!filters) return false
|
||||
|
||||
if (baseColumnHasAnyFilter(filters)) return true
|
||||
|
||||
if (filters.notifications && filters.notifications.participating) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
filters.notifications &&
|
||||
filterRecordHasAnyForcedValue(filters.notifications.reasons)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function getFilteredIssueOrPullRequests(
|
||||
items: EnhancedGitHubIssueOrPullRequest[],
|
||||
filters: IssueOrPullRequestColumnFilters | undefined,
|
||||
) {
|
||||
let _items = sortIssuesOrPullRequests(items)
|
||||
|
||||
if (filters && issueOrPullRequestColumnHasAnyFilter(filters)) {
|
||||
_items = _items.filter(item => {
|
||||
const subjectType = getIssueOrPullRequestSubjectType(item)
|
||||
|
||||
if (!itemPassesFilterRecord(filters.subjectTypes!, subjectType!, true))
|
||||
return false
|
||||
|
||||
if (
|
||||
typeof filters.unread === 'boolean' &&
|
||||
filters.unread !== !isItemRead(item)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
const showSaveForLater = filters.saved !== false
|
||||
const showInbox = filters.saved !== true
|
||||
const showCleared = false
|
||||
|
||||
if (
|
||||
filters.clearedAt &&
|
||||
(!item.updated_at || item.updated_at <= filters.clearedAt)
|
||||
)
|
||||
if (!(showSaveForLater && item.saved))
|
||||
/* && isItemRead(notification) */
|
||||
return showCleared
|
||||
|
||||
if (item.saved) return showSaveForLater
|
||||
|
||||
return showInbox
|
||||
})
|
||||
}
|
||||
|
||||
return _items
|
||||
}
|
||||
|
||||
export function getFilteredNotifications(
|
||||
notifications: EnhancedGitHubNotification[],
|
||||
filters: NotificationColumnFilters | undefined,
|
||||
) {
|
||||
let _notifications = sortNotifications(notifications)
|
||||
|
||||
const reasonsFilter =
|
||||
filters && filters.notifications && filters.notifications.reasons
|
||||
|
||||
if (filters && notificationColumnHasAnyFilter(filters)) {
|
||||
_notifications = _notifications.filter(item => {
|
||||
const subjectType = getNotificationSubjectType(item)
|
||||
|
||||
if (!itemPassesFilterRecord(reasonsFilter!, item.reason, true))
|
||||
return false
|
||||
|
||||
if (
|
||||
filters.notifications &&
|
||||
filters.notifications.participating &&
|
||||
item.reason === 'subscribed'
|
||||
)
|
||||
return false
|
||||
|
||||
if (!itemPassesFilterRecord(filters.subjectTypes!, subjectType!, true))
|
||||
return false
|
||||
|
||||
if (
|
||||
typeof filters.unread === 'boolean' &&
|
||||
filters.unread !== !isItemRead(item)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
typeof filters.private === 'boolean' &&
|
||||
isNotificationPrivate(item) !== filters.private
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
const showSaveForLater = filters.saved !== false
|
||||
const showInbox = filters.saved !== true
|
||||
const showCleared = false
|
||||
|
||||
if (
|
||||
filters.clearedAt &&
|
||||
(!item.updated_at || item.updated_at <= filters.clearedAt)
|
||||
)
|
||||
if (!(showSaveForLater && item.saved))
|
||||
/* && isItemRead(notification) */
|
||||
return showCleared
|
||||
|
||||
if (item.saved) return showSaveForLater
|
||||
|
||||
return showInbox
|
||||
})
|
||||
}
|
||||
|
||||
return _notifications
|
||||
}
|
||||
|
||||
export function getFilteredEvents(
|
||||
events: EnhancedGitHubEvent[],
|
||||
filters: ActivityColumnFilters | undefined,
|
||||
mergeSimilar: boolean,
|
||||
) {
|
||||
let _events = sortEvents(events)
|
||||
|
||||
const actionFilter = filters && filters.activity && filters.activity.actions
|
||||
|
||||
if (filters && activityColumnHasAnyFilter(filters)) {
|
||||
_events = _events.filter(item => {
|
||||
const subjectType = getEventSubjectType(item)
|
||||
|
||||
if (!itemPassesFilterRecord(filters.subjectTypes!, subjectType!, true))
|
||||
return false
|
||||
|
||||
if (
|
||||
!itemPassesFilterRecord(
|
||||
actionFilter!,
|
||||
getEventMetadata(item).action!,
|
||||
true,
|
||||
)
|
||||
)
|
||||
return false
|
||||
|
||||
if (
|
||||
typeof filters.unread === 'boolean' &&
|
||||
filters.unread !== !isItemRead(item)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
typeof filters.private === 'boolean' &&
|
||||
isEventPrivate(item) !== filters.private
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
const showSaveForLater = filters.saved !== false
|
||||
const showInbox = filters.saved !== true
|
||||
const showCleared = false
|
||||
|
||||
if (
|
||||
filters.clearedAt &&
|
||||
(!item.created_at || item.created_at <= filters.clearedAt)
|
||||
)
|
||||
if (!(showSaveForLater && item.saved))
|
||||
/* && isItemRead(event) */
|
||||
return showCleared
|
||||
|
||||
if (item.saved) return showSaveForLater
|
||||
|
||||
return showInbox
|
||||
})
|
||||
}
|
||||
|
||||
return mergeSimilar ? mergeSimilarEvents(_events, mergeMaxLength) : _events
|
||||
}
|
||||
|
||||
export const mergeMaxLength = 5
|
||||
@@ -5,18 +5,22 @@ import {
|
||||
EnhancedGitHubEvent,
|
||||
GitHubEventAction,
|
||||
GitHubEventSubjectType,
|
||||
GitHubIcon,
|
||||
GitHubIssue,
|
||||
GitHubPullRequest,
|
||||
MultipleStarEvent,
|
||||
ThemeColors,
|
||||
} from '../../types'
|
||||
import { getBranchNameFromRef, isDraft, isPullRequest } from './shared'
|
||||
|
||||
export function getOlderEventDate(
|
||||
events: EnhancedGitHubEvent[],
|
||||
field: keyof EnhancedGitHubEvent = 'created_at',
|
||||
) {
|
||||
const olderItem = sortEvents(events, field, 'desc').pop()
|
||||
return olderItem && olderItem[field]
|
||||
}
|
||||
import {
|
||||
getBranchNameFromRef,
|
||||
getCommitIconAndColor,
|
||||
getIssueIconAndColor,
|
||||
getPullRequestIconAndColor,
|
||||
getReleaseIconAndColor,
|
||||
getTagIconAndColor,
|
||||
isDraft,
|
||||
isPullRequest,
|
||||
} from './shared'
|
||||
|
||||
export const eventActions: GitHubEventAction[] = [
|
||||
'added',
|
||||
@@ -35,6 +39,27 @@ export const eventActions: GitHubEventAction[] = [
|
||||
'updated',
|
||||
]
|
||||
|
||||
export const eventSubjectTypes: GitHubEventSubjectType[] = [
|
||||
'Branch',
|
||||
'Commit',
|
||||
'Issue',
|
||||
'PullRequest',
|
||||
'PullRequestReview',
|
||||
'Release',
|
||||
'Repository',
|
||||
'Tag',
|
||||
'User',
|
||||
'Wiki',
|
||||
]
|
||||
|
||||
export function getOlderEventDate(
|
||||
events: EnhancedGitHubEvent[],
|
||||
field: keyof EnhancedGitHubEvent = 'created_at',
|
||||
) {
|
||||
const olderItem = sortEvents(events, field, 'desc').pop()
|
||||
return olderItem && olderItem[field]
|
||||
}
|
||||
|
||||
export function getEventActionMetadata<T extends GitHubEventAction>(
|
||||
action: T,
|
||||
): { label: string; action: T } {
|
||||
@@ -635,3 +660,216 @@ export function sortEvents(
|
||||
.orderBy(field, order)
|
||||
.value()
|
||||
}
|
||||
|
||||
export function getEventSubjectType(
|
||||
event: EnhancedGitHubEvent,
|
||||
): GitHubEventSubjectType | null {
|
||||
if (!(event && event.type)) return null
|
||||
|
||||
switch (event.type) {
|
||||
case 'CommitCommentEvent':
|
||||
return 'Commit'
|
||||
|
||||
case 'CreateEvent':
|
||||
case 'DeleteEvent': {
|
||||
switch (event.payload.ref_type) {
|
||||
case 'repository':
|
||||
return 'Repository'
|
||||
case 'branch':
|
||||
return 'Branch'
|
||||
case 'tag':
|
||||
return 'Tag'
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
case 'ForkEvent':
|
||||
return 'Repository'
|
||||
case 'GollumEvent':
|
||||
return 'Wiki'
|
||||
case 'IssueCommentEvent':
|
||||
return 'Issue'
|
||||
case 'IssuesEvent':
|
||||
return 'Issue'
|
||||
case 'MemberEvent':
|
||||
return 'User'
|
||||
case 'PublicEvent':
|
||||
return 'Repository'
|
||||
case 'PullRequestEvent':
|
||||
return 'PullRequest'
|
||||
case 'PullRequestReviewCommentEvent':
|
||||
return 'PullRequestReview'
|
||||
case 'PullRequestReviewEvent':
|
||||
return 'PullRequestReview'
|
||||
case 'PushEvent':
|
||||
return 'Commit'
|
||||
case 'ReleaseEvent':
|
||||
return 'Release'
|
||||
case 'WatchEvent':
|
||||
case 'WatchEvent:OneUserMultipleRepos':
|
||||
return 'Repository'
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function getEventIconAndColor(
|
||||
event: EnhancedGitHubEvent,
|
||||
): { color?: keyof ThemeColors; icon: GitHubIcon; subIcon?: GitHubIcon } {
|
||||
switch (event.type) {
|
||||
case 'CommitCommentEvent':
|
||||
return {
|
||||
...getCommitIconAndColor(),
|
||||
subIcon: 'comment-discussion',
|
||||
}
|
||||
|
||||
case 'CreateEvent': {
|
||||
switch (event.payload.ref_type) {
|
||||
case 'repository':
|
||||
return { icon: 'repo' }
|
||||
case 'branch':
|
||||
return { icon: 'git-branch' }
|
||||
case 'tag':
|
||||
return { icon: 'tag' }
|
||||
default:
|
||||
return { icon: 'plus' }
|
||||
}
|
||||
}
|
||||
|
||||
case 'DeleteEvent': {
|
||||
switch (event.payload.ref_type) {
|
||||
case 'repository':
|
||||
return { icon: 'repo', color: 'red' }
|
||||
case 'branch':
|
||||
return { icon: 'git-branch', color: 'red' }
|
||||
case 'tag':
|
||||
return { icon: 'tag', color: 'red' }
|
||||
default:
|
||||
return { icon: 'trashcan' }
|
||||
}
|
||||
}
|
||||
|
||||
case 'ForkEvent':
|
||||
return { icon: 'repo-forked' }
|
||||
|
||||
case 'GollumEvent':
|
||||
return { icon: 'book' }
|
||||
|
||||
case 'IssueCommentEvent': {
|
||||
return {
|
||||
...(isPullRequest(event.payload.issue)
|
||||
? getPullRequestIconAndColor(event.payload.issue as GitHubPullRequest)
|
||||
: getIssueIconAndColor(event.payload.issue)),
|
||||
subIcon: 'comment-discussion',
|
||||
}
|
||||
}
|
||||
|
||||
case 'IssuesEvent': {
|
||||
const issue = event.payload.issue
|
||||
|
||||
switch (event.payload.action) {
|
||||
case 'opened':
|
||||
return getIssueIconAndColor({ state: 'open' } as GitHubIssue)
|
||||
case 'closed':
|
||||
return getIssueIconAndColor({ state: 'closed' } as GitHubIssue)
|
||||
|
||||
case 'reopened':
|
||||
return {
|
||||
...getIssueIconAndColor({ state: 'open' } as GitHubIssue),
|
||||
icon: 'issue-reopened',
|
||||
}
|
||||
// case 'assigned':
|
||||
// case 'unassigned':
|
||||
// case 'labeled':
|
||||
// case 'unlabeled':
|
||||
// case 'edited':
|
||||
// case 'milestoned':
|
||||
// case 'demilestoned':
|
||||
default:
|
||||
return getIssueIconAndColor(issue)
|
||||
}
|
||||
}
|
||||
case 'MemberEvent':
|
||||
return { icon: 'person' }
|
||||
|
||||
case 'PublicEvent':
|
||||
return { icon: 'globe', color: 'blue' }
|
||||
|
||||
case 'PullRequestEvent': {
|
||||
const pullRequest = event.payload.pull_request
|
||||
|
||||
switch (event.payload.action) {
|
||||
case 'opened':
|
||||
case 'reopened':
|
||||
return getPullRequestIconAndColor({
|
||||
draft: pullRequest.draft,
|
||||
state: 'open',
|
||||
merged: false,
|
||||
merged_at: undefined,
|
||||
mergeable_state: pullRequest.mergeable_state,
|
||||
})
|
||||
// case 'closed': return getPullRequestIconAndColor({ state: 'closed' } as GitHubPullRequest);
|
||||
|
||||
// case 'assigned':
|
||||
// case 'unassigned':
|
||||
// case 'labeled':
|
||||
// case 'unlabeled':
|
||||
// case 'edited':
|
||||
default:
|
||||
return getPullRequestIconAndColor(pullRequest)
|
||||
}
|
||||
}
|
||||
|
||||
case 'PullRequestReviewCommentEvent':
|
||||
case 'PullRequestReviewEvent': {
|
||||
return {
|
||||
...getPullRequestIconAndColor(event.payload.pull_request),
|
||||
subIcon: 'comment-discussion',
|
||||
}
|
||||
}
|
||||
|
||||
case 'PushEvent':
|
||||
return { icon: 'code' }
|
||||
|
||||
case 'ReleaseEvent':
|
||||
return isTagMainEvent(event)
|
||||
? getTagIconAndColor()
|
||||
: getReleaseIconAndColor()
|
||||
|
||||
case 'WatchEvent':
|
||||
case 'WatchEvent:OneUserMultipleRepos':
|
||||
return { icon: 'star', color: 'yellow' }
|
||||
|
||||
default: {
|
||||
const message = `Unknown event type: ${(event as any).type}`
|
||||
console.error(message)
|
||||
return { icon: 'mark-github' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeEventsPreservingEnhancement(
|
||||
newItems: EnhancedGitHubEvent[],
|
||||
prevItems: EnhancedGitHubEvent[],
|
||||
) {
|
||||
return sortEvents(
|
||||
_.uniqBy(_.concat(newItems, prevItems), 'id').map(item => {
|
||||
const newItem = newItems.find(i => i.id === item.id)
|
||||
const existingItem = prevItems.find(i => i.id === item.id)
|
||||
if (!(newItem && existingItem)) return item
|
||||
|
||||
const mergedItem = {
|
||||
forceUnreadLocally: existingItem.forceUnreadLocally,
|
||||
last_read_at: existingItem.last_read_at,
|
||||
last_unread_at: existingItem.last_unread_at,
|
||||
saved: existingItem.saved,
|
||||
unread: existingItem.unread,
|
||||
...newItem,
|
||||
}
|
||||
|
||||
return _.isEqual(mergedItem, existingItem) ? existingItem : mergedItem
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,14 +4,58 @@ import _ from 'lodash'
|
||||
import {
|
||||
EnhancedGitHubIssueOrPullRequest,
|
||||
EnhancementCache,
|
||||
GitHubIssue,
|
||||
GitHubIssueOrPullRequest,
|
||||
GitHubIssueOrPullRequestSubjectType,
|
||||
GitHubPullRequest,
|
||||
IssueOrPullRequestColumnSubscription,
|
||||
IssueOrPullRequestPayloadEnhancement,
|
||||
} from '../../types'
|
||||
import { getOwnerAndRepo } from './shared'
|
||||
import {
|
||||
getIssueIconAndColor,
|
||||
getOwnerAndRepo,
|
||||
getPullRequestIconAndColor,
|
||||
} from './shared'
|
||||
import { getRepoFullNameFromUrl } from './url'
|
||||
|
||||
export const issueOrPullRequestSubjectTypes: GitHubIssueOrPullRequestSubjectType[] = [
|
||||
'Issue',
|
||||
'PullRequest',
|
||||
]
|
||||
|
||||
export function getIssueOrPullRequestIconAndColor(
|
||||
type: GitHubIssueOrPullRequestSubjectType,
|
||||
issueOrPullRequest: GitHubIssueOrPullRequest,
|
||||
) {
|
||||
return type === 'PullRequest'
|
||||
? getPullRequestIconAndColor(issueOrPullRequest as GitHubPullRequest)
|
||||
: getIssueIconAndColor(issueOrPullRequest as GitHubIssue)
|
||||
}
|
||||
|
||||
export function mergeIssuesOrPullRequestsPreservingEnhancement(
|
||||
newItems: EnhancedGitHubIssueOrPullRequest[],
|
||||
prevItems: EnhancedGitHubIssueOrPullRequest[],
|
||||
) {
|
||||
return sortIssuesOrPullRequests(
|
||||
_.uniqBy(_.concat(newItems || [], prevItems || []), 'id').map(item => {
|
||||
const newItem = (newItems || []).find(i => i.id === item.id)
|
||||
const existingItem = prevItems.find(i => i.id === item.id)
|
||||
if (!(newItem && existingItem)) return item
|
||||
|
||||
const mergedItem = {
|
||||
forceUnreadLocally: existingItem.forceUnreadLocally,
|
||||
last_read_at: existingItem.last_read_at,
|
||||
last_unread_at: existingItem.last_unread_at,
|
||||
saved: existingItem.saved,
|
||||
unread: existingItem.unread,
|
||||
...newItem,
|
||||
}
|
||||
|
||||
return _.isEqual(mergedItem, existingItem) ? existingItem : mergedItem
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
export function getIssueOrPullRequestSubjectType(
|
||||
item: GitHubIssueOrPullRequest,
|
||||
): GitHubIssueOrPullRequestSubjectType | null {
|
||||
@@ -185,7 +229,7 @@ export function getGitHubIssueSearchQuery(
|
||||
if (repoFullName) queryArr.push(`repo:${repoFullName}`)
|
||||
|
||||
if (subjectType === 'Issue') queryArr.push('is:issue')
|
||||
if (subjectType === 'PullRequest') queryArr.push('is:pr')
|
||||
else if (subjectType === 'PullRequest') queryArr.push('is:pr')
|
||||
|
||||
return queryArr.join(' ')
|
||||
}
|
||||
|
||||
@@ -4,12 +4,241 @@ import _ from 'lodash'
|
||||
import {
|
||||
EnhancedGitHubNotification,
|
||||
EnhancementCache,
|
||||
GitHubIcon,
|
||||
GitHubIssue,
|
||||
GitHubIssueOrPullRequest,
|
||||
GitHubNotification,
|
||||
GitHubNotificationReason,
|
||||
GitHubNotificationSubjectType,
|
||||
GitHubPullRequest,
|
||||
NotificationPayloadEnhancement,
|
||||
ThemeColors,
|
||||
} from '../../types'
|
||||
import { getOwnerAndRepo } from './shared'
|
||||
import { capitalize } from '../shared'
|
||||
import {
|
||||
getCommitIconAndColor,
|
||||
getIssueIconAndColor,
|
||||
getOwnerAndRepo,
|
||||
getPullRequestIconAndColor,
|
||||
getReleaseIconAndColor,
|
||||
} from './shared'
|
||||
import { getCommentIdFromUrl } from './url'
|
||||
|
||||
export const notificationReasons: GitHubNotificationReason[] = [
|
||||
'assign',
|
||||
'author',
|
||||
'comment',
|
||||
'invitation',
|
||||
'manual',
|
||||
'mention',
|
||||
'review_requested',
|
||||
'security_alert',
|
||||
'state_change',
|
||||
'subscribed',
|
||||
'team_mention',
|
||||
]
|
||||
|
||||
export const notificationSubjectTypes: GitHubNotificationSubjectType[] = [
|
||||
'Commit',
|
||||
'Issue',
|
||||
'PullRequest',
|
||||
'Release',
|
||||
'RepositoryInvitation',
|
||||
'RepositoryVulnerabilityAlert',
|
||||
]
|
||||
|
||||
export function getNotificationSubjectType(
|
||||
notification: GitHubNotification,
|
||||
): GitHubNotificationSubjectType | null {
|
||||
if (!(notification && notification.subject && notification.subject.type))
|
||||
return null
|
||||
|
||||
return notification.subject.type
|
||||
}
|
||||
|
||||
export function getNotificationIconAndColor(
|
||||
notification: GitHubNotification,
|
||||
payload: GitHubIssueOrPullRequest | undefined,
|
||||
): { icon: GitHubIcon; color?: keyof ThemeColors; tooltip: string } {
|
||||
const { subject } = notification
|
||||
const { type } = subject
|
||||
|
||||
switch (type) {
|
||||
case 'Commit':
|
||||
return getCommitIconAndColor()
|
||||
case 'Issue':
|
||||
return getIssueIconAndColor(payload as GitHubIssue)
|
||||
case 'PullRequest':
|
||||
return getPullRequestIconAndColor(payload as GitHubPullRequest)
|
||||
case 'Release':
|
||||
return getReleaseIconAndColor()
|
||||
case 'RepositoryInvitation':
|
||||
return {
|
||||
icon: 'mail',
|
||||
color: 'brown',
|
||||
tooltip: 'Repository invitation',
|
||||
}
|
||||
case 'RepositoryVulnerabilityAlert':
|
||||
return {
|
||||
icon: 'alert',
|
||||
color: 'yellow',
|
||||
tooltip: 'Repository vulnerability alert',
|
||||
}
|
||||
default: {
|
||||
const message = `Unknown event type: ${(event as any).type}`
|
||||
console.error(message)
|
||||
return { icon: 'bell', tooltip: '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getNotificationReasonMetadata<
|
||||
T extends GitHubNotificationReason
|
||||
>(
|
||||
reason: T,
|
||||
): {
|
||||
color: keyof ThemeColors
|
||||
reason: T
|
||||
label: string
|
||||
fullDescription: string
|
||||
// smallDescription: string
|
||||
} {
|
||||
switch (reason) {
|
||||
case 'assign':
|
||||
return {
|
||||
reason,
|
||||
color: 'pink',
|
||||
fullDescription: 'You were assigned to the thread',
|
||||
// smallDescription: 'You were assigned',
|
||||
label: 'Assigned',
|
||||
}
|
||||
|
||||
case 'author':
|
||||
return {
|
||||
reason,
|
||||
color: 'lightRed',
|
||||
fullDescription: 'You created the thread',
|
||||
// smallDescription: 'You created',
|
||||
label: 'Author',
|
||||
}
|
||||
|
||||
case 'comment':
|
||||
return {
|
||||
reason,
|
||||
color: 'blue',
|
||||
fullDescription: 'You commented on the thread',
|
||||
// smallDescription: 'You commented',
|
||||
label: 'Commented',
|
||||
}
|
||||
|
||||
case 'invitation':
|
||||
return {
|
||||
reason,
|
||||
color: 'brown',
|
||||
fullDescription:
|
||||
'You accepted an invitation to contribute to the repository',
|
||||
// smallDescription: 'You were invited',
|
||||
label: 'Invited',
|
||||
}
|
||||
|
||||
case 'manual':
|
||||
return {
|
||||
reason,
|
||||
color: 'teal',
|
||||
fullDescription: 'You manually subscribed to the thread',
|
||||
// smallDescription: 'You subscribed manually',
|
||||
label: 'Manual',
|
||||
}
|
||||
|
||||
case 'mention':
|
||||
return {
|
||||
reason,
|
||||
color: 'orange',
|
||||
fullDescription: 'You were @mentioned in the thread',
|
||||
// smallDescription: 'You were mentioned',
|
||||
label: 'Mentioned',
|
||||
}
|
||||
|
||||
case 'state_change':
|
||||
return {
|
||||
reason,
|
||||
color: 'purple',
|
||||
fullDescription: 'You opened or closed the issue/pr',
|
||||
// smallDescription: 'You opened/closed',
|
||||
label: 'State changed',
|
||||
}
|
||||
|
||||
case 'subscribed':
|
||||
return {
|
||||
reason,
|
||||
color: 'blueGray',
|
||||
fullDescription: "You're watching the repository",
|
||||
// smallDescription: 'You are watching',
|
||||
label: 'Watching',
|
||||
}
|
||||
|
||||
case 'team_mention':
|
||||
return {
|
||||
reason,
|
||||
color: 'yellow',
|
||||
fullDescription: 'Your team was mentioned in the thread',
|
||||
// smallDescription: 'Team mentioned',
|
||||
label: 'Team mentioned',
|
||||
}
|
||||
|
||||
case 'review_requested':
|
||||
return {
|
||||
reason,
|
||||
color: 'yellow',
|
||||
fullDescription: 'Someone requested your review in the pull request',
|
||||
// smallDescription: 'Review requested',
|
||||
label: 'Review requested',
|
||||
}
|
||||
|
||||
case 'security_alert':
|
||||
return {
|
||||
reason,
|
||||
color: 'red',
|
||||
fullDescription: 'Potential security vulnerability found',
|
||||
// smallDescription: 'Security alert',
|
||||
label: 'Security',
|
||||
}
|
||||
|
||||
default:
|
||||
return {
|
||||
reason,
|
||||
color: 'gray',
|
||||
fullDescription: '',
|
||||
// smallDescription: '',
|
||||
label: capitalize(reason),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeNotificationsPreservingEnhancement(
|
||||
newItems: EnhancedGitHubNotification[],
|
||||
prevItems: EnhancedGitHubNotification[],
|
||||
) {
|
||||
return sortNotifications(
|
||||
_.uniqBy(_.concat(newItems, prevItems), 'id').map(item => {
|
||||
const newItem = newItems.find(i => i.id === item.id)
|
||||
const existingItem = prevItems.find(i => i.id === item.id)
|
||||
if (!(newItem && existingItem)) return item
|
||||
|
||||
const mergedItem = {
|
||||
forceUnreadLocally: existingItem.forceUnreadLocally,
|
||||
last_read_at: existingItem.last_read_at,
|
||||
last_unread_at: existingItem.last_unread_at,
|
||||
saved: existingItem.saved,
|
||||
unread: existingItem.unread,
|
||||
...newItem,
|
||||
}
|
||||
|
||||
return _.isEqual(mergedItem, existingItem) ? existingItem : mergedItem
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
export async function getNotificationsEnhancementMap(
|
||||
notifications: EnhancedGitHubNotification[],
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import gravatar from 'gravatar'
|
||||
import _ from 'lodash'
|
||||
import qs from 'qs'
|
||||
|
||||
import {
|
||||
@@ -10,10 +11,13 @@ import {
|
||||
EnhancedGitHubIssueOrPullRequest,
|
||||
EnhancedGitHubNotification,
|
||||
GitHubAPIHeaders,
|
||||
GitHubEventSubjectType,
|
||||
GitHubIcon,
|
||||
GitHubNotificationSubjectType,
|
||||
GitHubPullRequest,
|
||||
IssueOrPullRequestColumnSubscription,
|
||||
NotificationColumnSubscription,
|
||||
ThemeColors,
|
||||
} from '../../types'
|
||||
import { getSteppedSize } from '../shared'
|
||||
import { getGitHubIssueSearchQuery } from './issues'
|
||||
@@ -526,3 +530,148 @@ export function getBranchNameFromRef(ref: string | undefined) {
|
||||
.slice(2)
|
||||
.join('/')
|
||||
}
|
||||
|
||||
export function getCommitIconAndColor(): {
|
||||
icon: GitHubIcon
|
||||
color?: keyof ThemeColors
|
||||
tooltip: string
|
||||
} {
|
||||
return { icon: 'git-commit', color: 'brown', tooltip: 'Commit' }
|
||||
}
|
||||
|
||||
export function getReleaseIconAndColor(): {
|
||||
icon: GitHubIcon
|
||||
color?: keyof ThemeColors
|
||||
tooltip: string
|
||||
} {
|
||||
return {
|
||||
icon: 'rocket',
|
||||
color: 'pink',
|
||||
tooltip: 'Release',
|
||||
}
|
||||
}
|
||||
|
||||
export function getTagIconAndColor(): {
|
||||
icon: GitHubIcon
|
||||
color?: keyof ThemeColors
|
||||
tooltip: string
|
||||
} {
|
||||
return {
|
||||
icon: 'tag',
|
||||
color: 'gray',
|
||||
tooltip: 'Tag',
|
||||
}
|
||||
}
|
||||
|
||||
export function getPullRequestIconAndColor(pullRequest: {
|
||||
draft: GitHubPullRequest['draft']
|
||||
state: GitHubPullRequest['state']
|
||||
merged: GitHubPullRequest['merged'] | undefined
|
||||
merged_at: GitHubPullRequest['merged_at'] | undefined
|
||||
mergeable_state: GitHubPullRequest['mergeable_state'] | undefined
|
||||
}): { icon: GitHubIcon; color?: keyof ThemeColors; tooltip: string } {
|
||||
const draft = isDraft(pullRequest)
|
||||
const merged = !!(pullRequest.merged || pullRequest.merged_at)
|
||||
const state = merged ? 'merged' : pullRequest.state
|
||||
|
||||
switch (state) {
|
||||
case 'open':
|
||||
return {
|
||||
icon: 'git-pull-request',
|
||||
color: draft ? 'gray' : 'green',
|
||||
tooltip: `Open${draft ? ' draft' : ''} pull request`,
|
||||
}
|
||||
|
||||
case 'closed':
|
||||
return {
|
||||
icon: 'git-pull-request',
|
||||
color: 'lightRed',
|
||||
tooltip: `Closed${draft ? ' draft' : ''} pull request`,
|
||||
}
|
||||
|
||||
case 'merged':
|
||||
return {
|
||||
icon: 'git-merge',
|
||||
color: 'purple',
|
||||
tooltip: `Merged pull request`,
|
||||
}
|
||||
|
||||
default:
|
||||
return {
|
||||
icon: 'git-pull-request',
|
||||
tooltip: 'Pull Request',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getIssueIconAndColor(issue: {
|
||||
state?: GitHubPullRequest['state']
|
||||
merged_at?: GitHubPullRequest['merged_at']
|
||||
}): { icon: GitHubIcon; color?: keyof ThemeColors; tooltip: string } {
|
||||
const { state } = issue
|
||||
|
||||
if (isPullRequest(issue)) {
|
||||
return getPullRequestIconAndColor(issue as GitHubPullRequest)
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case 'open':
|
||||
return {
|
||||
icon: 'issue-opened',
|
||||
color: 'green',
|
||||
tooltip: `Open issue`,
|
||||
}
|
||||
|
||||
case 'closed':
|
||||
return {
|
||||
icon: 'issue-closed',
|
||||
color: 'lightRed',
|
||||
tooltip: 'Closed issue',
|
||||
}
|
||||
|
||||
default:
|
||||
return { icon: 'issue-opened', tooltip: 'Issue' }
|
||||
}
|
||||
}
|
||||
|
||||
export function getSubjectTypeMetadata<
|
||||
T extends GitHubEventSubjectType | GitHubNotificationSubjectType
|
||||
>(
|
||||
subjectType: T,
|
||||
): {
|
||||
color?: keyof ThemeColors
|
||||
label: string
|
||||
subjectType: T
|
||||
} {
|
||||
switch (
|
||||
subjectType as GitHubEventSubjectType | GitHubNotificationSubjectType
|
||||
) {
|
||||
case 'PullRequestReview': {
|
||||
return {
|
||||
label: 'Review',
|
||||
subjectType,
|
||||
}
|
||||
}
|
||||
|
||||
case 'RepositoryInvitation': {
|
||||
return {
|
||||
label: 'Invitation',
|
||||
subjectType,
|
||||
}
|
||||
}
|
||||
|
||||
case 'RepositoryVulnerabilityAlert': {
|
||||
return {
|
||||
label: 'Security Alert',
|
||||
subjectType,
|
||||
}
|
||||
}
|
||||
|
||||
default: {
|
||||
return {
|
||||
label: _.startCase(subjectType),
|
||||
subjectType,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './filters'
|
||||
export * from './github'
|
||||
export * from './graphql'
|
||||
export * from './shared'
|
||||
|
||||
@@ -187,6 +187,7 @@ export interface GitHubIssue {
|
||||
title: string
|
||||
labels: GitHubLabel[]
|
||||
state: 'open' | 'closed'
|
||||
draft?: boolean
|
||||
locked: boolean
|
||||
milestone?: GitHubMilestone | null
|
||||
comments: number
|
||||
|
||||
Reference in New Issue
Block a user