-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
feat(issues): Two-column activity icons, colors #115958
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a30141e
020cba6
d50ef02
3ce6f5a
15060e5
79712b0
930a12e
567cf7f
997a2fb
fde9e8e
1e24e9f
2c03ec2
608ffbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,8 @@ | ||
| import {Fragment, useCallback, useState} from 'react'; | ||
| import {useTheme} from '@emotion/react'; | ||
| import {useTheme, type Theme} from '@emotion/react'; | ||
| import styled from '@emotion/styled'; | ||
|
|
||
| import {SentryAppAvatar, UserAvatar} from '@sentry/scraps/avatar'; | ||
| import {LinkButton} from '@sentry/scraps/button'; | ||
| import {Flex, Grid} from '@sentry/scraps/layout'; | ||
| import {Text} from '@sentry/scraps/text'; | ||
|
|
@@ -47,6 +48,74 @@ function getAuthorName(item: GroupActivity) { | |
| return 'Sentry'; | ||
| } | ||
|
|
||
| function getActivityMarker(item: GroupActivity, color: string) { | ||
| if (item.sentry_app) { | ||
| return ( | ||
| <AvatarMarker color={color}> | ||
| <SentryAppAvatar | ||
| data-test-id="sentry-app-activity-marker" | ||
| sentryApp={item.sentry_app} | ||
| size={22} | ||
| /> | ||
| </AvatarMarker> | ||
| ); | ||
| } | ||
| if (item.user) { | ||
| return ( | ||
| <AvatarMarker color={color}> | ||
| <UserAvatar data-test-id="user-activity-marker" user={item.user} size={22} /> | ||
| </AvatarMarker> | ||
| ); | ||
| } | ||
| return <SentryMarker color={color} data-test-id="sentry-activity-marker" />; | ||
| } | ||
|
|
||
| function getActivityColorConfig(theme: Theme, type: GroupActivityType) { | ||
| const defaultConfig = { | ||
| title: theme.tokens.content.primary, | ||
| icon: theme.tokens.content.secondary, | ||
| iconBorder: theme.tokens.content.secondary, | ||
| }; | ||
|
|
||
| switch (type) { | ||
| case GroupActivityType.SET_RESOLVED: | ||
| case GroupActivityType.SET_RESOLVED_BY_AGE: | ||
| case GroupActivityType.SET_RESOLVED_IN_RELEASE: | ||
| case GroupActivityType.SET_RESOLVED_IN_COMMIT: | ||
| case GroupActivityType.SET_RESOLVED_IN_PULL_REQUEST: | ||
| case GroupActivityType.MARK_REVIEWED: | ||
| case GroupActivityType.SEER_RCA_COMPLETED: | ||
| case GroupActivityType.SEER_SOLUTION_COMPLETED: | ||
| case GroupActivityType.SEER_CODING_COMPLETED: | ||
| case GroupActivityType.SEER_PR_CREATED: | ||
| return { | ||
| ...defaultConfig, | ||
| icon: theme.tokens.graphics.success.vibrant, | ||
| iconBorder: theme.tokens.border.success.vibrant, | ||
| }; | ||
| case GroupActivityType.SET_UNRESOLVED: | ||
| case GroupActivityType.SET_REGRESSION: | ||
| return { | ||
| ...defaultConfig, | ||
| icon: theme.tokens.graphics.danger.vibrant, | ||
| iconBorder: theme.tokens.border.danger.vibrant, | ||
| }; | ||
| case GroupActivityType.SET_ESCALATING: | ||
| case GroupActivityType.SET_PRIORITY: | ||
| return { | ||
| ...defaultConfig, | ||
| icon: theme.tokens.graphics.warning.vibrant, | ||
| iconBorder: theme.tokens.border.warning.vibrant, | ||
| }; | ||
| case GroupActivityType.SET_IGNORED: | ||
| return { | ||
| ...defaultConfig, | ||
| }; | ||
| default: | ||
| return defaultConfig; | ||
| } | ||
| } | ||
|
|
||
| function TimelineItem({ | ||
| item, | ||
| handleDelete, | ||
|
|
@@ -65,8 +134,11 @@ function TimelineItem({ | |
| teams: Team[]; | ||
| }) { | ||
| const organization = useOrganization(); | ||
| const theme = useTheme(); | ||
| const [editing, setEditing] = useState(false); | ||
| const useTwoColumnLayout = organization.features.includes('issue-activity-feed-v2'); | ||
| const authorName = getAuthorName(item); | ||
| const colorConfig = getActivityColorConfig(theme, item.type); | ||
| const {title, message} = getGroupActivityItem( | ||
| item, | ||
| organization, | ||
|
|
@@ -77,8 +149,12 @@ function TimelineItem({ | |
| ); | ||
|
|
||
| const iconMapping = groupActivityTypeIconMapping[item.type]; | ||
| const Icon = iconMapping?.componentFunction | ||
| ? iconMapping.componentFunction({ | ||
| const componentFunction = | ||
| useTwoColumnLayout && item.type === GroupActivityType.NOTE | ||
| ? undefined | ||
| : iconMapping?.componentFunction; | ||
| const Icon = componentFunction | ||
| ? componentFunction({ | ||
| data: item.data, | ||
| user: item.user, | ||
| sentry_app: item.sentry_app, | ||
|
|
@@ -102,6 +178,8 @@ function TimelineItem({ | |
| </Flex> | ||
| } | ||
| timestamp={<Timestamp date={item.dateCreated} tooltipProps={{skipWrapper: true}} />} | ||
| marker={useTwoColumnLayout ? getActivityMarker(item, colorConfig.icon) : undefined} | ||
| colorConfig={useTwoColumnLayout ? colorConfig : undefined} | ||
| icon={ | ||
| Icon && ( | ||
| <Icon | ||
|
|
@@ -302,6 +380,7 @@ export function ActivitySection({ | |
| item => !filterComments || item.type === GroupActivityType.NOTE | ||
| ); | ||
| const inputVariant = variant === 'sidebar' ? 'compact' : 'full'; | ||
| const useTwoColumnLayout = organization.features.includes('issue-activity-feed-v2'); | ||
|
|
||
| const renderActivityItem = (item: GroupActivity) => ( | ||
| <TimelineItem | ||
|
|
@@ -379,6 +458,7 @@ export function ActivitySection({ | |
| {t('View %s more', filteredActivities.length - 3)} | ||
| </LinkButton> | ||
| } | ||
| marker={useTwoColumnLayout ? <span /> : undefined} | ||
| icon={<RotatedEllipsisIcon direction="up" />} | ||
| /> | ||
| </Fragment> | ||
|
|
@@ -398,7 +478,6 @@ const TitleTooltip = styled(Tooltip)` | |
|
|
||
| const ActivityTimelineItem = styled(Timeline.Item)` | ||
| align-items: center; | ||
| grid-template-columns: 22px minmax(50px, 1fr) auto; | ||
| `; | ||
|
|
||
| const Timestamp = styled(TimeSince)` | ||
|
|
@@ -418,3 +497,36 @@ const NoteWrapper = styled('div')<{size: 'sm' | 'md'}>` | |
| const ActivityInputFrame = styled('div')` | ||
| color: ${p => p.theme.tokens.content.primary}; | ||
| `; | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sidebar "View more" row misaligned with marker rowsMedium Severity When the Reviewed by Cursor Bugbot for commit fde9e8e. Configure here. |
||
| const AvatarMarker = styled('span')<{color: string}>` | ||
| display: block; | ||
| position: relative; | ||
| border-radius: 100%; | ||
| line-height: 0; | ||
|
|
||
| &::after { | ||
| content: ''; | ||
| position: absolute; | ||
| inset: 0; | ||
| border-radius: 100%; | ||
| box-shadow: inset 0 0 0 2px ${p => p.color}; | ||
| pointer-events: none; | ||
| } | ||
| `; | ||
|
|
||
| const SentryMarker = styled('span')<{color: string}>` | ||
| width: 12px; | ||
| height: 12px; | ||
| border-radius: 100%; | ||
| background: ${p => p.theme.tokens.background.primary}; | ||
| display: grid; | ||
| place-items: center; | ||
|
|
||
| &::after { | ||
| content: ''; | ||
| width: 6px; | ||
| height: 6px; | ||
| border-radius: 100%; | ||
| background: ${p => p.color}; | ||
| } | ||
| `; | ||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SET_RESOLVED_IN_PULL_REQUESTmissing from success color configMedium Severity
The
getActivityColorConfigswitch statement includes allSET_RESOLVED_*activity types (SET_RESOLVED,SET_RESOLVED_BY_AGE,SET_RESOLVED_IN_RELEASE,SET_RESOLVED_IN_COMMIT) in the success/green color group — but omitsGroupActivityType.SET_RESOLVED_IN_PULL_REQUEST. This means resolved-by-PR activities won't receive the green icon and border that all other resolved activity types get, resulting in an inconsistent visual treatment.Reviewed by Cursor Bugbot for commit fde9e8e. Configure here.