chore(lint): fix typecheck errors from hoisting refactors

- pair-scan: type BARCODE_SCANNER_SETTINGS as BarcodeSettings
- explorer-sidebar: add missing desktopSidebarStyle useMemo
- sidebar-workspace-list: coerce null dotColor to transparent
- menu-header, e2e.test: formatting
This commit is contained in:
Mohamed Boudra
2026-04-24 02:17:56 +07:00
parent e730d13957
commit 5b420dbfdb
5 changed files with 64 additions and 53 deletions

View File

@@ -4,7 +4,7 @@ import { useLocalSearchParams, useRouter } from "expo-router";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { CameraView, useCameraPermissions } from "expo-camera";
import type { BarcodeScanningResult } from "expo-camera";
import type { BarcodeScanningResult, BarcodeSettings } from "expo-camera";
import { useHostMutations } from "@/runtime/host-runtime";
import { decodeOfferFragmentPayload, normalizeHostPort } from "@/utils/daemon-endpoints";
import { connectToDaemon } from "@/utils/test-daemon-connection";
@@ -272,7 +272,7 @@ export default function PairScanScreen() {
);
}
const BARCODE_SCANNER_SETTINGS = { barcodeTypes: ["qr"] as const };
const BARCODE_SCANNER_SETTINGS: BarcodeSettings = { barcodeTypes: ["qr"] };
const CORNER_TL_STYLE = [styles.corner, styles.cornerTL];
const CORNER_TR_STYLE = [styles.corner, styles.cornerTR];
const CORNER_BL_STYLE = [styles.corner, styles.cornerBL];

View File

@@ -279,6 +279,10 @@ export function ExplorerSidebar({
mobileKeyboardInsetStyle,
],
);
const desktopSidebarStyle = useMemo(
() => [explorerStaticStyles.desktopSidebar, resizeAnimatedStyle, { paddingTop: insets.top }],
[resizeAnimatedStyle, insets.top],
);
// Mobile: full-screen overlay with gesture.
// On web, keep it interactive only while open so closed sidebars don't eat taps.

View File

@@ -27,10 +27,7 @@ const MOBILE_MENU_LINE_SHORT_WIDTH = 8;
const MOBILE_MENU_LINE_HEIGHT = 2;
function MobileMenuIcon({ color }: { color: string }) {
const lineStyle = useMemo(
() => [styles.mobileMenuLine, { backgroundColor: color }],
[color],
);
const lineStyle = useMemo(() => [styles.mobileMenuLine, { backgroundColor: color }], [color]);
const shortLineStyle = useMemo(
() => [styles.mobileMenuLine, styles.mobileMenuLineShort, { backgroundColor: color }],
[color],

View File

@@ -230,6 +230,11 @@ export function PrBadge({ hint }: { hint: PrHint }) {
const handleHoverIn = useCallback(() => setIsHovered(true), []);
const handleHoverOut = useCallback(() => setIsHovered(false), []);
const prBadgeTextStyle = useMemo(
() => [prBadgeStyles.text, { color: activeColor }],
[activeColor],
);
return (
<Pressable
accessibilityRole="link"
@@ -246,7 +251,7 @@ export function PrBadge({ hint }: { hint: PrHint }) {
) : (
<GitPullRequest size={12} color={iconColor} />
)}
<Text style={[prBadgeStyles.text, { color: activeColor }]} numberOfLines={1}>
<Text style={prBadgeTextStyle} numberOfLines={1}>
#{hint.number}
</Text>
</Pressable>
@@ -290,18 +295,20 @@ const prBadgeStyles = StyleSheet.create((theme) => ({
function ChecksBadge({ checks }: { checks: PrHint["checks"] }): ReactElement | null {
const { theme } = useUnistyles();
const color = theme.colors.palette.red[500];
const textStyle = useMemo(() => [checksBadgeStyles.text, { color }], [color]);
if (!checks || checks.length === 0) return null;
const failed = checks.filter((c) => c.status === "failure").length;
if (failed === 0) return null;
const color = theme.colors.palette.red[500];
const label = `${failed} failed`;
return (
<View style={checksBadgeStyles.badge}>
<GitHubIcon size={10} color={color} />
<Text style={[checksBadgeStyles.text, { color }]}>{label}</Text>
<Text style={textStyle}>{label}</Text>
</View>
);
}
@@ -375,24 +382,25 @@ function WorkspaceStatusIndicator({
? EMPHASIZED_STATUS_DOT_OFFSET
: DEFAULT_STATUS_DOT_OFFSET;
const statusDotOverlayStyle = useMemo(
() => [
styles.statusDotOverlay,
{
backgroundColor: dotColor ?? undefined,
borderColor: theme.colors.surface0,
width: statusDotSize,
height: statusDotSize,
right: statusDotOffset,
bottom: statusDotOffset,
},
],
[dotColor, theme.colors.surface0, statusDotSize, statusDotOffset],
);
return (
<View style={styles.workspaceStatusDot}>
<KindIcon size={14} color={theme.colors.foregroundMuted} />
{dotColor ? (
<View
style={[
styles.statusDotOverlay,
{
backgroundColor: dotColor,
borderColor: theme.colors.surface0,
width: statusDotSize,
height: statusDotSize,
right: statusDotOffset,
bottom: statusDotOffset,
},
]}
/>
) : null}
{dotColor ? <View style={statusDotOverlayStyle} /> : null}
</View>
);
}
@@ -430,8 +438,9 @@ function ProjectLeadingVisual({
);
}
const iconSource = useMemo(() => ({ uri: iconDataUri ?? "" }), [iconDataUri]);
const projectIcon = iconDataUri ? (
<Image source={{ uri: iconDataUri }} style={styles.projectIcon} />
<Image source={iconSource} style={styles.projectIcon} />
) : (
<View style={styles.projectIconFallback}>
<Text style={styles.projectIconFallbackText}>{placeholderInitial}</Text>
@@ -486,24 +495,25 @@ function ProjectLeadingVisual({
? EMPHASIZED_STATUS_DOT_OFFSET
: DEFAULT_STATUS_DOT_OFFSET;
const projectStatusDotOverlayStyle = useMemo(
() => [
styles.statusDotOverlay,
{
backgroundColor: dotColor ?? "transparent",
borderColor: theme.colors.surface0,
width: statusDotSize,
height: statusDotSize,
right: statusDotOffset,
bottom: statusDotOffset,
},
],
[dotColor, theme.colors.surface0, statusDotSize, statusDotOffset],
);
return (
<View style={styles.projectLeadingVisualSlot}>
{projectIcon}
{dotColor ? (
<View
style={[
styles.statusDotOverlay,
{
backgroundColor: dotColor,
borderColor: theme.colors.surface0,
width: statusDotSize,
height: statusDotSize,
right: statusDotOffset,
bottom: statusDotOffset,
},
]}
/>
) : null}
{dotColor ? <View style={projectStatusDotOverlayStyle} /> : null}
</View>
);
}
@@ -1059,6 +1069,16 @@ function WorkspaceRowInner({
(s) => s.lifecycle === "running" && (s.type ?? "service") === "service",
);
const accessibilityState = useMemo(() => ({ selected }), [selected]);
const workspaceBranchTextStyle = useMemo(
() => [
styles.workspaceBranchText,
isHovered && styles.workspaceBranchTextHovered,
isCreating && styles.workspaceBranchTextCreating,
],
[isHovered, isCreating],
);
return (
<WorkspaceHoverCard workspace={workspace} prHint={prHint} isDragging={isDragging}>
<View
@@ -1073,7 +1093,7 @@ function WorkspaceRowInner({
disabled={isArchiving}
aria-selected={selected}
accessibilityRole="button"
accessibilityState={{ selected }}
accessibilityState={accessibilityState}
style={workspaceRowStyle}
onPressIn={interaction.handlePressIn}
onTouchMove={interaction.handleTouchMove}
@@ -1088,14 +1108,7 @@ function WorkspaceRowInner({
workspaceKind={workspace.workspaceKind}
loading={isArchiving || isCreating}
/>
<Text
style={[
styles.workspaceBranchText,
isHovered && styles.workspaceBranchTextHovered,
isCreating && styles.workspaceBranchTextCreating,
]}
numberOfLines={1}
>
<Text style={workspaceBranchTextStyle} numberOfLines={1}>
{workspace.name}
</Text>
</View>

View File

@@ -155,10 +155,7 @@ async function waitForRelayWebSocketReady(
return poll();
}
async function waitForProcessExit(
relayProcess: ChildProcess,
deadline: number,
): Promise<void> {
async function waitForProcessExit(relayProcess: ChildProcess, deadline: number): Promise<void> {
if (relayProcess.exitCode !== null) return;
if (Date.now() >= deadline) return;
await sleep(50);