diff --git a/apps/ios/App.tsx b/apps/ios/App.tsx index a4deff4a3aa..05481036c31 100644 --- a/apps/ios/App.tsx +++ b/apps/ios/App.tsx @@ -32,6 +32,7 @@ import { quality } from './qualityNative'; import { qualityVisibility } from './qualityView'; import { parseFabricProofLink, type FabricProofInput } from './fabricProof'; import { DiagnosticsBoundary } from './DiagnosticsBoundary'; +import { ClientControlNative } from './ClientControlNative'; // The chrome is native: one UITabBarController (react-native-screens' tabs, through // @react-navigation/bottom-tabs' native navigator) holding a UINavigationController per tab @@ -244,7 +245,7 @@ function AppContent() {
} /> : -
+
} ; } diff --git a/apps/ios/ClientControlIndicator.tsx b/apps/ios/ClientControlIndicator.tsx new file mode 100644 index 00000000000..dfa1bb10399 --- /dev/null +++ b/apps/ios/ClientControlIndicator.tsx @@ -0,0 +1,38 @@ +import { StyleSheet, View } from 'react-native'; +import { theme } from './theme'; +import { Button, T, useTextAppearance } from './ui'; + +/** The app-internal view of an active client-control grant; not a wire type. */ +export type ClientControlGrant = { runId: string }; + +/** + * Shown while a run may drive this client: what is controlling it, and how to stop it. + * Renders nothing without a grant. Test-mode only until a generated grant source exists. + */ +export function ClientControlIndicator({ grant, onRevoke }: { grant: ClientControlGrant | undefined; onRevoke: () => void }) { + const appearance = useTextAppearance(); + if (!grant) return null; + return ( + + + Controlled by run + {grant.runId} + +