Show connected host daemon versions on the About page (#1220)

* Show connected host daemon versions on the About page

* Update About E2E assertion for the App version label
This commit is contained in:
Mohamed Boudra
2026-05-29 14:51:53 +08:00
committed by GitHub
parent 38f0560f9b
commit 69d36c76fe
2 changed files with 113 additions and 12 deletions
+1 -1
View File
@@ -156,7 +156,7 @@ export async function expectDiagnosticsContent(page: Page): Promise<void> {
} }
export async function expectAboutContent(page: Page): Promise<void> { export async function expectAboutContent(page: Page): Promise<void> {
await expect(page.getByText("Version", { exact: true }).first()).toBeVisible(); await expect(page.getByText("App version", { exact: true }).first()).toBeVisible();
} }
export async function expectGeneralContent(page: Page): Promise<void> { export async function expectGeneralContent(page: Page): Promise<void> {
+112 -11
View File
@@ -45,7 +45,14 @@ import {
type Settings as EffectiveSettings, type Settings as EffectiveSettings,
} from "@/hooks/use-settings"; } from "@/hooks/use-settings";
import { THEME_SWATCHES } from "@/styles/theme"; import { THEME_SWATCHES } from "@/styles/theme";
import { getHostRuntimeStore, isHostRuntimeConnected, useHosts } from "@/runtime/host-runtime"; import {
getHostRuntimeStore,
isHostRuntimeConnected,
useHostRuntimeIsConnected,
useHosts,
} from "@/runtime/host-runtime";
import { useSessionStore } from "@/stores/session-store";
import type { HostProfile } from "@/types/host-connection";
import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region"; import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region";
import { useWindowControlsPadding } from "@/utils/desktop-window"; import { useWindowControlsPadding } from "@/utils/desktop-window";
import { confirmDialog } from "@/utils/confirm-dialog"; import { confirmDialog } from "@/utils/confirm-dialog";
@@ -442,29 +449,114 @@ function DiagnosticsSection({
} }
interface AboutSectionProps { interface AboutSectionProps {
appVersion: string | null;
appVersionText: string; appVersionText: string;
isDesktopApp: boolean; isDesktopApp: boolean;
} }
function AboutSection({ appVersionText, isDesktopApp }: AboutSectionProps) { function AboutSection({ appVersion, appVersionText, isDesktopApp }: AboutSectionProps) {
return ( return (
<SettingsSection title="About"> <>
<View style={settingsStyles.card}> <SettingsSection title="About">
<View style={settingsStyles.row}> <View style={settingsStyles.card}>
<View style={settingsStyles.rowContent}> <View style={settingsStyles.row}>
<Text style={settingsStyles.rowTitle}>Version</Text> <View style={settingsStyles.rowContent}>
<Text style={settingsStyles.rowTitle}>App version</Text>
<Text style={settingsStyles.rowHint}>This device</Text>
</View>
<Text style={styles.aboutValue}>{appVersionText}</Text>
</View> </View>
<Text style={styles.aboutValue}>{appVersionText}</Text> {isDesktopApp ? <DesktopAppUpdateRow /> : null}
</View> </View>
{isDesktopApp ? <DesktopAppUpdateRow /> : null} </SettingsSection>
</View> <ConnectedHostsSection clientVersion={appVersion} />
<View style={styles.aboutCommunity}> <View style={styles.aboutCommunity}>
<CommunityLinks /> <CommunityLinks />
</View> </View>
</>
);
}
function normalizeVersion(version: string | null | undefined): string | null {
const trimmed = version?.trim();
if (!trimmed) return null;
return trimmed.replace(/^v/i, "");
}
function ConnectedHostsSection({ clientVersion }: { clientVersion: string | null }) {
const hosts = useHosts();
if (hosts.length === 0) {
return null;
}
return (
<SettingsSection title="Connected hosts">
<View style={settingsStyles.card}>
{hosts.map((host, index) => (
<HostVersionRow
key={host.serverId}
host={host}
showBorder={index > 0}
clientVersion={clientVersion}
/>
))}
</View>
</SettingsSection> </SettingsSection>
); );
} }
function HostVersionRow({
host,
showBorder,
clientVersion,
}: {
host: HostProfile;
showBorder: boolean;
clientVersion: string | null;
}) {
const isConnected = useHostRuntimeIsConnected(host.serverId);
const daemonVersion = useSessionStore(
(state) => state.sessions[host.serverId]?.serverInfo?.version ?? null,
);
const rowStyle = useMemo(
() => [settingsStyles.row, showBorder && settingsStyles.rowBorder],
[showBorder],
);
const normalizedHost = normalizeVersion(daemonVersion);
const normalizedClient = normalizeVersion(clientVersion);
const isMismatch =
normalizedHost !== null && normalizedClient !== null && normalizedHost !== normalizedClient;
let valueText: string;
if (!isConnected) {
valueText = "Offline";
} else if (normalizedHost) {
valueText = formatVersionWithPrefix(normalizedHost);
} else {
valueText = "—";
}
const valueStyle = useMemo(
() => [styles.aboutValue, isMismatch && styles.aboutVersionMismatch],
[isMismatch],
);
return (
<View style={rowStyle}>
<View style={settingsStyles.rowContent}>
<Text style={settingsStyles.rowTitle} numberOfLines={1}>
{host.label}
</Text>
{isMismatch ? (
<Text style={settingsStyles.rowHint}>Version differs from this device</Text>
) : null}
</View>
<Text style={valueStyle}>{valueText}</Text>
</View>
);
}
function getUpdateButtonLabel( function getUpdateButtonLabel(
isInstalling: boolean, isInstalling: boolean,
latestVersion: string | null | undefined, latestVersion: string | null | undefined,
@@ -1106,7 +1198,13 @@ export default function SettingsScreen({ view }: SettingsScreenProps) {
/> />
); );
case "about": case "about":
return <AboutSection appVersionText={appVersionText} isDesktopApp={isDesktopApp} />; return (
<AboutSection
appVersion={appVersion}
appVersionText={appVersionText}
isDesktopApp={isDesktopApp}
/>
);
} }
} }
return null; return null;
@@ -1266,6 +1364,9 @@ const styles = StyleSheet.create((theme) => ({
color: theme.colors.foregroundMuted, color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.sm, fontSize: theme.fontSize.sm,
}, },
aboutVersionMismatch: {
color: theme.colors.palette.amber[500],
},
aboutErrorText: { aboutErrorText: {
color: theme.colors.palette.red[300], color: theme.colors.palette.red[300],
fontSize: theme.fontSize.xs, fontSize: theme.fontSize.xs,