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:
@@ -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> {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user