Show all PR check counts in hover card (#993)
This commit is contained in:
@@ -8,18 +8,10 @@ import {
|
|||||||
type ReactElement,
|
type ReactElement,
|
||||||
type ReactNode,
|
type ReactNode,
|
||||||
} from "react";
|
} from "react";
|
||||||
import {
|
import { Dimensions, Platform, Text, View } from "react-native";
|
||||||
Dimensions,
|
|
||||||
Platform,
|
|
||||||
Text,
|
|
||||||
View,
|
|
||||||
type StyleProp,
|
|
||||||
type TextStyle,
|
|
||||||
type ViewStyle,
|
|
||||||
} from "react-native";
|
|
||||||
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
|
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
|
||||||
import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
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 { GitHubIcon } from "@/components/icons/github-icon";
|
||||||
import type { Theme } from "@/styles/theme";
|
import type { Theme } from "@/styles/theme";
|
||||||
import { DiffStat } from "@/components/diff-stat";
|
import { DiffStat } from "@/components/diff-stat";
|
||||||
@@ -316,9 +308,62 @@ function WorkspaceHoverCardContent({
|
|||||||
|
|
||||||
const ThemedExternalLink = withUnistyles(ExternalLink);
|
const ThemedExternalLink = withUnistyles(ExternalLink);
|
||||||
const ThemedGitHubIcon = withUnistyles(GitHubIcon);
|
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 foregroundColorMapping = (theme: Theme) => ({ color: theme.colors.foreground });
|
||||||
const foregroundMutedColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted });
|
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<PrHint["checks"]>) {
|
||||||
|
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 (
|
||||||
|
<View style={styles.checksSummaryPill}>
|
||||||
|
<ThemedCircleCheck size={12} uniProps={successColorMapping} />
|
||||||
|
<Text style={styles.checksStatusTextPassed}>{count}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind === "failed") {
|
||||||
|
return (
|
||||||
|
<View style={styles.checksSummaryPill}>
|
||||||
|
<ThemedCircleX size={12} uniProps={dangerColorMapping} />
|
||||||
|
<Text style={styles.checksStatusTextFailed}>{count}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.checksSummaryPill}>
|
||||||
|
<ThemedCircleDot size={12} uniProps={warningColorMapping} />
|
||||||
|
<Text style={styles.checksStatusTextPending}>{count}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function ChecksSummaryContent({
|
function ChecksSummaryContent({
|
||||||
checks,
|
checks,
|
||||||
@@ -327,26 +372,7 @@ function ChecksSummaryContent({
|
|||||||
checks: NonNullable<PrHint["checks"]>;
|
checks: NonNullable<PrHint["checks"]>;
|
||||||
hovered: boolean;
|
hovered: boolean;
|
||||||
}) {
|
}) {
|
||||||
const failed = checks.filter((c) => c.status === "failure").length;
|
const { passed, failed, pending } = getChecksSummaryCounts(checks);
|
||||||
const pending = checks.filter((c) => c.status === "pending").length;
|
|
||||||
|
|
||||||
let badgeLabel: string;
|
|
||||||
let dotStyle: StyleProp<ViewStyle>;
|
|
||||||
let statusTextStyle: StyleProp<TextStyle>;
|
|
||||||
|
|
||||||
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 labelStyle = hovered ? checksSummaryLabelHoveredCombined : styles.checksSummaryLabel;
|
const labelStyle = hovered ? checksSummaryLabelHoveredCombined : styles.checksSummaryLabel;
|
||||||
const iconUniProps = hovered ? foregroundColorMapping : foregroundMutedColorMapping;
|
const iconUniProps = hovered ? foregroundColorMapping : foregroundMutedColorMapping;
|
||||||
@@ -360,8 +386,9 @@ function ChecksSummaryContent({
|
|||||||
)}
|
)}
|
||||||
<Text style={labelStyle}>Checks</Text>
|
<Text style={labelStyle}>Checks</Text>
|
||||||
<View style={styles.checksSummaryCounts}>
|
<View style={styles.checksSummaryCounts}>
|
||||||
<View style={dotStyle} />
|
<ChecksSummaryPill count={passed} kind="passed" />
|
||||||
<Text style={statusTextStyle}>{badgeLabel}</Text>
|
<ChecksSummaryPill count={failed} kind="failed" />
|
||||||
|
<ChecksSummaryPill count={pending} kind="pending" />
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -465,42 +492,29 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
checksSummaryCounts: {
|
checksSummaryCounts: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 4,
|
gap: theme.spacing[2],
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: "flex-end",
|
justifyContent: "flex-end",
|
||||||
},
|
},
|
||||||
checksDotFailed: {
|
checksSummaryPill: {
|
||||||
width: 6,
|
flexDirection: "row",
|
||||||
height: 6,
|
alignItems: "center",
|
||||||
borderRadius: 3,
|
gap: 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],
|
|
||||||
},
|
},
|
||||||
checksStatusTextFailed: {
|
checksStatusTextFailed: {
|
||||||
fontSize: theme.fontSize.xs,
|
fontSize: theme.fontSize.xs,
|
||||||
fontWeight: theme.fontWeight.normal,
|
fontWeight: theme.fontWeight.normal,
|
||||||
color: theme.colors.palette.red[500],
|
color: theme.colors.statusDanger,
|
||||||
},
|
},
|
||||||
checksStatusTextPending: {
|
checksStatusTextPending: {
|
||||||
fontSize: theme.fontSize.xs,
|
fontSize: theme.fontSize.xs,
|
||||||
fontWeight: theme.fontWeight.normal,
|
fontWeight: theme.fontWeight.normal,
|
||||||
color: theme.colors.palette.amber[500],
|
color: theme.colors.statusWarning,
|
||||||
},
|
},
|
||||||
checksStatusTextPassed: {
|
checksStatusTextPassed: {
|
||||||
fontSize: theme.fontSize.xs,
|
fontSize: theme.fontSize.xs,
|
||||||
fontWeight: theme.fontWeight.normal,
|
fontWeight: theme.fontWeight.normal,
|
||||||
color: theme.colors.palette.green[500],
|
color: theme.colors.statusSuccess,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user