fix(ux): 10 UI/UX bugs from pre-release audit (toward no-bug launch gate)
Found via parallel screen audit; each confirmed in code: - AuthGate: auto-prompt biometrics on lock (useEffect was imported but unused) - CodeBlock: horizontal scroll for long code lines (were wrapped/mangled) - DiffView: horizontal scroll instead of numberOfLines=1 truncation - chat: biometric-cancel on send shows feedback instead of silently dropping msg - chat: send failure restores input + attachments and alerts - chat: removed dead /compact + /clear builtin commands (advertised, no-op) - sessions: delete + rename failures alert instead of silent; rename guarded against double-submit - sessions: onRefresh spinner no longer hangs forever if a refresh rejects - add/edit connection: validate URL has http(s):// scheme before save/test typecheck clean, 65/65 unit tests pass. Runtime UI behavior still needs on-device verification per the pre-posting test gate (HANDOFF §0b). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,16 @@ export function AuthGate({ children }: Props) {
|
||||
|
||||
const { isAuthenticated, settings, hasBiometrics, biometricType, authenticate, error } = useAuth()
|
||||
|
||||
// Auto-prompt the OS biometric dialog once when the lock screen appears, so users
|
||||
// aren't forced to tap "Unlock" on every cold start. If they cancel/fail, the
|
||||
// manual "Unlock" button below is the fallback.
|
||||
useEffect(() => {
|
||||
if (settings.requireBiometric && hasBiometrics && !isAuthenticated) {
|
||||
authenticate()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
// If biometric not required, or no biometrics available, show children
|
||||
if (!settings.requireBiometric || !hasBiometrics) {
|
||||
return <>{children}</>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { View, Text, StyleSheet, Platform } from "react-native"
|
||||
import { View, Text, StyleSheet, Platform, ScrollView } from "react-native"
|
||||
|
||||
const mono = Platform.OS === "ios" ? "Menlo" : "monospace"
|
||||
|
||||
@@ -76,32 +76,35 @@ export function DiffView({ before, after, isDark }: Props) {
|
||||
|
||||
return (
|
||||
<View style={[s.container, isDark && s.containerDark]}>
|
||||
{lines.map((line, idx) => (
|
||||
<View
|
||||
key={idx}
|
||||
style={[
|
||||
s.line,
|
||||
line.type === "add" && (isDark ? s.addDark : s.add),
|
||||
line.type === "remove" && (isDark ? s.removeDark : s.remove),
|
||||
]}
|
||||
>
|
||||
<Text style={[s.prefix, isDark && s.prefixDark]}>
|
||||
{line.type === "add" ? "+" : line.type === "remove" ? "-" : " "}
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
s.text,
|
||||
isDark && s.textDark,
|
||||
line.type === "add" && s.addText,
|
||||
line.type === "remove" && s.removeText,
|
||||
]}
|
||||
selectable
|
||||
numberOfLines={1}
|
||||
>
|
||||
{line.text}
|
||||
</Text>
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator>
|
||||
<View>
|
||||
{lines.map((line, idx) => (
|
||||
<View
|
||||
key={idx}
|
||||
style={[
|
||||
s.line,
|
||||
line.type === "add" && (isDark ? s.addDark : s.add),
|
||||
line.type === "remove" && (isDark ? s.removeDark : s.remove),
|
||||
]}
|
||||
>
|
||||
<Text style={[s.prefix, isDark && s.prefixDark]}>
|
||||
{line.type === "add" ? "+" : line.type === "remove" ? "-" : " "}
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
s.text,
|
||||
isDark && s.textDark,
|
||||
line.type === "add" && s.addText,
|
||||
line.type === "remove" && s.removeText,
|
||||
]}
|
||||
selectable
|
||||
>
|
||||
{line.text}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -135,7 +138,6 @@ const s = StyleSheet.create({
|
||||
prefixDark: { color: "#666666" },
|
||||
|
||||
text: {
|
||||
flex: 1,
|
||||
fontSize: 12,
|
||||
fontFamily: mono,
|
||||
color: "#0a0a0a",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from "react"
|
||||
import { View, Text, TouchableOpacity, StyleSheet, useColorScheme, Platform } from "react-native"
|
||||
import { View, Text, TouchableOpacity, StyleSheet, useColorScheme, Platform, ScrollView } from "react-native"
|
||||
import * as Clipboard from "expo-clipboard"
|
||||
|
||||
interface Props {
|
||||
@@ -27,9 +27,11 @@ export function CodeBlock({ code, language }: Props) {
|
||||
<Text style={[styles.copyBtn, isDark && styles.copyBtnDark]}>{copied ? "Copied!" : "Copy"}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<Text style={[styles.code, isDark && styles.codeDark]} selectable>
|
||||
{code}
|
||||
</Text>
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator contentContainerStyle={styles.codeScroll}>
|
||||
<Text style={[styles.code, isDark && styles.codeDark]} selectable>
|
||||
{code}
|
||||
</Text>
|
||||
</ScrollView>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -72,12 +74,14 @@ const styles = StyleSheet.create({
|
||||
copyBtnDark: {
|
||||
color: "#a78bfa",
|
||||
},
|
||||
codeScroll: {
|
||||
padding: 12,
|
||||
},
|
||||
code: {
|
||||
fontFamily: Platform.OS === "ios" ? "Menlo" : "monospace",
|
||||
fontSize: 13,
|
||||
lineHeight: 20,
|
||||
color: "#1a1a1a",
|
||||
padding: 12,
|
||||
},
|
||||
codeDark: {
|
||||
color: "#e5e5e5",
|
||||
|
||||
Reference in New Issue
Block a user