Polish desktop settings update/local-daemon sections

This commit is contained in:
Mohamed Boudra
2026-02-22 13:28:14 +07:00
parent 44c3ee670f
commit 8ee9e7274d
5 changed files with 297 additions and 259 deletions
@@ -1,5 +1,4 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { HostProfile } from '@/contexts/daemon-registry-context'
async function loadModuleForPlatform(platform: 'web' | 'ios' | 'android') {
vi.resetModules()
@@ -7,47 +6,6 @@ async function loadModuleForPlatform(platform: 'web' | 'ios' | 'android') {
return import('./desktop-updates')
}
function createHost(input: {
serverId: string
endpoint: string
type?: 'direct' | 'relay'
}): HostProfile {
const now = new Date(0).toISOString()
if (input.type === 'relay') {
return {
serverId: input.serverId,
label: input.serverId,
connections: [
{
id: `${input.serverId}-relay`,
type: 'relay',
relayEndpoint: input.endpoint,
daemonPublicKeyB64: 'test-key',
},
],
preferredConnectionId: `${input.serverId}-relay`,
createdAt: now,
updatedAt: now,
}
}
return {
serverId: input.serverId,
label: input.serverId,
connections: [
{
id: `${input.serverId}-direct`,
type: 'direct',
endpoint: input.endpoint,
},
],
preferredConnectionId: `${input.serverId}-direct`,
createdAt: now,
updatedAt: now,
}
}
describe('desktop-updates helpers', () => {
afterEach(() => {
vi.doUnmock('react-native')
@@ -79,20 +37,47 @@ describe('desktop-updates helpers', () => {
expect(formatVersionWithPrefix(null)).toBe('\u2014')
})
it('prefers localhost direct host when selecting local daemon', async () => {
const { findLikelyLocalDaemonHost } = await loadModuleForPlatform('web')
const remote = createHost({ serverId: 'remote', endpoint: '10.0.0.2:6767' })
const local = createHost({ serverId: 'local', endpoint: 'localhost:6767' })
it('parses valid local daemon version result', async () => {
const { parseLocalDaemonVersionResult } = await loadModuleForPlatform('web')
expect(findLikelyLocalDaemonHost([remote, local])?.serverId).toBe('local')
expect(parseLocalDaemonVersionResult({ version: '0.1.15', error: null })).toEqual({
version: '0.1.15',
error: null,
})
})
it('falls back to first host when no localhost endpoint exists', async () => {
const { findLikelyLocalDaemonHost } = await loadModuleForPlatform('web')
const relay = createHost({ serverId: 'relay', endpoint: 'relay.paseo.sh:443', type: 'relay' })
const remote = createHost({ serverId: 'remote', endpoint: '10.0.0.2:6767' })
it('parses local daemon version error result', async () => {
const { parseLocalDaemonVersionResult } = await loadModuleForPlatform('web')
expect(findLikelyLocalDaemonHost([relay, remote])?.serverId).toBe('relay')
expect(
parseLocalDaemonVersionResult({ version: null, error: 'paseo command not found in PATH' })
).toEqual({
version: null,
error: 'paseo command not found in PATH',
})
})
it('parses unexpected local daemon version result', async () => {
const { parseLocalDaemonVersionResult } = await loadModuleForPlatform('web')
expect(parseLocalDaemonVersionResult(null)).toEqual({
version: null,
error: 'Unexpected response from version check.',
})
expect(parseLocalDaemonVersionResult('not an object')).toEqual({
version: null,
error: 'Unexpected response from version check.',
})
})
it('trims whitespace in parsed version', async () => {
const { parseLocalDaemonVersionResult } = await loadModuleForPlatform('web')
expect(parseLocalDaemonVersionResult({ version: ' 0.1.15 ', error: null })).toEqual({
version: '0.1.15',
error: null,
})
})
it('builds copyable daemon update diagnostics', async () => {
@@ -1,5 +1,4 @@
import { Platform } from 'react-native'
import type { HostProfile } from '@/contexts/daemon-registry-context'
import { getTauri } from '@/utils/tauri'
import { invokeDesktopCommand } from '@/desktop/tauri/invoke-desktop-command'
@@ -23,6 +22,11 @@ export interface LocalDaemonUpdateResult {
stderr: string
}
export interface LocalDaemonVersionResult {
version: string | null
error: string | null
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null
}
@@ -44,24 +48,26 @@ function toNumberOr(defaultValue: number, value: unknown): number {
return typeof value === 'number' && Number.isFinite(value) ? value : defaultValue
}
function isLikelyLocalDirectEndpoint(endpoint: string): boolean {
const normalized = endpoint.trim().toLowerCase()
return (
normalized === 'localhost' ||
normalized.startsWith('localhost:') ||
normalized === '127.0.0.1' ||
normalized.startsWith('127.0.0.1:') ||
normalized === '::1' ||
normalized.startsWith('::1:') ||
normalized === '[::1]' ||
normalized.startsWith('[::1]:')
)
}
export function shouldShowDesktopUpdateSection(): boolean {
return Platform.OS === 'web' && getTauri() !== null
}
export function parseLocalDaemonVersionResult(raw: unknown): LocalDaemonVersionResult {
if (!isRecord(raw)) {
return { version: null, error: 'Unexpected response from version check.' }
}
return {
version: toStringOrNull(raw.version),
error: toStringOrNull(raw.error),
}
}
export async function getLocalDaemonVersion(): Promise<LocalDaemonVersionResult> {
const result = await invokeDesktopCommand<unknown>('get_local_daemon_version')
return parseLocalDaemonVersionResult(result)
}
export async function checkDesktopAppUpdate(): Promise<DesktopAppUpdateCheckResult> {
const result = await invokeDesktopCommand<unknown>('check_app_update')
if (!isRecord(result)) {
@@ -135,27 +141,6 @@ export function formatVersionWithPrefix(version: string | null | undefined): str
return value.startsWith('v') ? value : `v${value}`
}
export function findLikelyLocalDaemonHost(daemons: HostProfile[]): HostProfile | null {
if (daemons.length === 0) {
return null
}
const localhostDaemon = daemons.find((daemon) => isLikelyLocalHost(daemon))
return localhostDaemon ?? daemons[0] ?? null
}
export function isLikelyLocalHost(host: HostProfile | null | undefined): boolean {
if (!host) {
return false
}
return host.connections.some(
(connection) =>
connection.type === 'direct' && isLikelyLocalDirectEndpoint(connection.endpoint)
)
}
export function buildDaemonUpdateDiagnostics(result: LocalDaemonUpdateResult): string {
const stdout = result.stdout.length > 0 ? result.stdout : '(empty)'
const stderr = result.stderr.length > 0 ? result.stderr : '(empty)'