diff --git a/packages/app/src/components/workspace-hover-card.tsx b/packages/app/src/components/workspace-hover-card.tsx index 672f5c808..9d28e3429 100644 --- a/packages/app/src/components/workspace-hover-card.tsx +++ b/packages/app/src/components/workspace-hover-card.tsx @@ -8,18 +8,10 @@ import { type ReactElement, type ReactNode, } from "react"; -import { - Dimensions, - Platform, - Text, - View, - type StyleProp, - type TextStyle, - type ViewStyle, -} from "react-native"; +import { Dimensions, Platform, Text, View } from "react-native"; import Animated, { FadeIn, FadeOut } from "react-native-reanimated"; import { StyleSheet, withUnistyles } from "react-native-unistyles"; -import { ExternalLink } from "lucide-react-native"; +import { CircleCheck, CircleDot, CircleX, ExternalLink } from "lucide-react-native"; import { GitHubIcon } from "@/components/icons/github-icon"; import type { Theme } from "@/styles/theme"; import { DiffStat } from "@/components/diff-stat"; @@ -316,9 +308,62 @@ function WorkspaceHoverCardContent({ const ThemedExternalLink = withUnistyles(ExternalLink); const ThemedGitHubIcon = withUnistyles(GitHubIcon); +const ThemedCircleCheck = withUnistyles(CircleCheck); +const ThemedCircleDot = withUnistyles(CircleDot); +const ThemedCircleX = withUnistyles(CircleX); const foregroundColorMapping = (theme: Theme) => ({ color: theme.colors.foreground }); const foregroundMutedColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted }); +const successColorMapping = (theme: Theme) => ({ color: theme.colors.statusSuccess }); +const warningColorMapping = (theme: Theme) => ({ color: theme.colors.statusWarning }); +const dangerColorMapping = (theme: Theme) => ({ color: theme.colors.statusDanger }); + +function getChecksSummaryCounts(checks: NonNullable) { + return checks.reduce( + (counts, check) => { + if (check.status === "success") counts.passed += 1; + else if (check.status === "failure") counts.failed += 1; + else if (check.status !== "skipped" && check.status !== "cancelled") counts.pending += 1; + return counts; + }, + { passed: 0, failed: 0, pending: 0 }, + ); +} + +function ChecksSummaryPill({ + count, + kind, +}: { + count: number; + kind: "passed" | "failed" | "pending"; +}) { + if (count === 0) return null; + + if (kind === "passed") { + return ( + + + {count} + + ); + } + + if (kind === "failed") { + return ( + + + {count} + + ); + } + + return ( + + + {count} + + ); +} function ChecksSummaryContent({ checks, @@ -327,26 +372,7 @@ function ChecksSummaryContent({ checks: NonNullable; hovered: boolean; }) { - const failed = checks.filter((c) => c.status === "failure").length; - const pending = checks.filter((c) => c.status === "pending").length; - - let badgeLabel: string; - let dotStyle: StyleProp; - let statusTextStyle: StyleProp; - - if (failed > 0) { - badgeLabel = `${failed} failed`; - dotStyle = styles.checksDotFailed; - statusTextStyle = styles.checksStatusTextFailed; - } else if (pending > 0) { - badgeLabel = `${pending} running`; - dotStyle = styles.checksDotPending; - statusTextStyle = styles.checksStatusTextPending; - } else { - badgeLabel = `${checks.length} passed`; - dotStyle = styles.checksDotPassed; - statusTextStyle = styles.checksStatusTextPassed; - } + const { passed, failed, pending } = getChecksSummaryCounts(checks); const labelStyle = hovered ? checksSummaryLabelHoveredCombined : styles.checksSummaryLabel; const iconUniProps = hovered ? foregroundColorMapping : foregroundMutedColorMapping; @@ -360,8 +386,9 @@ function ChecksSummaryContent({ )} Checks - - {badgeLabel} + + + ); @@ -465,42 +492,29 @@ const styles = StyleSheet.create((theme) => ({ checksSummaryCounts: { flexDirection: "row", alignItems: "center", - gap: 4, + gap: theme.spacing[2], flex: 1, justifyContent: "flex-end", }, - checksDotFailed: { - width: 6, - height: 6, - borderRadius: 3, - backgroundColor: theme.colors.palette.red[500], - }, - checksDotPending: { - width: 6, - height: 6, - borderRadius: 3, - backgroundColor: theme.colors.palette.amber[500], - }, - checksDotPassed: { - width: 6, - height: 6, - borderRadius: 3, - backgroundColor: theme.colors.palette.green[500], + checksSummaryPill: { + flexDirection: "row", + alignItems: "center", + gap: 3, }, checksStatusTextFailed: { fontSize: theme.fontSize.xs, fontWeight: theme.fontWeight.normal, - color: theme.colors.palette.red[500], + color: theme.colors.statusDanger, }, checksStatusTextPending: { fontSize: theme.fontSize.xs, fontWeight: theme.fontWeight.normal, - color: theme.colors.palette.amber[500], + color: theme.colors.statusWarning, }, checksStatusTextPassed: { fontSize: theme.fontSize.xs, fontWeight: theme.fontWeight.normal, - color: theme.colors.palette.green[500], + color: theme.colors.statusSuccess, }, }));