Refactor socket handlers to improve performance and enhance thinking level management
This commit is contained in:
@@ -3,13 +3,18 @@ import { useMemo, useState } from "react";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
Modal,
|
||||
Platform,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { colors, monoFont } from "@/lib/theme";
|
||||
import type { ModelRef, ModelSummary } from "@/lib/types";
|
||||
|
||||
@@ -39,6 +44,10 @@ export function ModelSelector({
|
||||
}: ModelSelectorProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [query, setQuery] = useState("");
|
||||
const { height: windowHeight } = useWindowDimensions();
|
||||
const insets = useSafeAreaInsets();
|
||||
const sheetHeight = Math.round(windowHeight * 0.78);
|
||||
|
||||
const filteredModels = useMemo(() => {
|
||||
const needle = query.trim().toLowerCase();
|
||||
if (!needle) {
|
||||
@@ -55,6 +64,17 @@ export function ModelSelector({
|
||||
? models.find((model) => model.provider === selected.provider && model.id === selected.id)
|
||||
: undefined;
|
||||
|
||||
const close = () => {
|
||||
Keyboard.dismiss();
|
||||
setQuery("");
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleSelect = (model: ModelRef) => {
|
||||
onSelect(model);
|
||||
close();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Pressable
|
||||
@@ -77,12 +97,24 @@ export function ModelSelector({
|
||||
)}
|
||||
</Pressable>
|
||||
|
||||
<Modal visible={open} animationType="slide" transparent onRequestClose={() => setOpen(false)}>
|
||||
<View style={styles.overlay}>
|
||||
<View style={styles.sheet}>
|
||||
<Modal visible={open} animationType="slide" transparent onRequestClose={close}>
|
||||
<KeyboardAvoidingView
|
||||
style={styles.overlay}
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
>
|
||||
<Pressable style={styles.backdrop} onPress={close} accessibilityLabel="Close model selector" />
|
||||
<View
|
||||
style={[
|
||||
styles.sheet,
|
||||
{
|
||||
height: sheetHeight,
|
||||
paddingBottom: Math.max(insets.bottom, 16),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.title}>Choose model</Text>
|
||||
<Pressable onPress={() => setOpen(false)} hitSlop={10}>
|
||||
<Pressable onPress={close} hitSlop={10} accessibilityLabel="Close">
|
||||
<Ionicons name="close" size={22} color={colors.textSecondary} />
|
||||
</Pressable>
|
||||
</View>
|
||||
@@ -94,15 +126,22 @@ export function ModelSelector({
|
||||
style={styles.search}
|
||||
autoCorrect={false}
|
||||
autoCapitalize="none"
|
||||
returnKeyType="search"
|
||||
/>
|
||||
{error ? <Text style={styles.error}>{error}</Text> : null}
|
||||
<View style={styles.listRegion}>
|
||||
{models.length === 0 ? (
|
||||
<Text style={styles.empty}>{loading ? "Loading models…" : "No available models"}</Text>
|
||||
) : (
|
||||
<FlatList
|
||||
style={styles.list}
|
||||
data={filteredModels}
|
||||
keyExtractor={(model) => `${model.provider}:${model.id}`}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
keyboardDismissMode="on-drag"
|
||||
contentContainerStyle={
|
||||
filteredModels.length === 0 ? styles.listEmptyContent : styles.listContent
|
||||
}
|
||||
renderItem={({ item }) => {
|
||||
const isSelected =
|
||||
item.provider === selected?.provider && item.id === selected?.id;
|
||||
@@ -113,10 +152,7 @@ export function ModelSelector({
|
||||
pressed && styles.pressed,
|
||||
isSelected && styles.selected,
|
||||
]}
|
||||
onPress={() => {
|
||||
onSelect({ provider: item.provider, id: item.id });
|
||||
setOpen(false);
|
||||
}}
|
||||
onPress={() => handleSelect({ provider: item.provider, id: item.id })}
|
||||
>
|
||||
<View style={styles.optionText}>
|
||||
<Text style={styles.optionId}>{item.id}</Text>
|
||||
@@ -136,6 +172,7 @@ export function ModelSelector({
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
@@ -160,14 +197,15 @@ const styles = StyleSheet.create({
|
||||
pressed: { backgroundColor: colors.surface },
|
||||
disabled: { opacity: 0.45 },
|
||||
overlay: { flex: 1, justifyContent: "flex-end", backgroundColor: "rgba(0, 0, 0, 0.65)" },
|
||||
backdrop: { ...StyleSheet.absoluteFillObject },
|
||||
sheet: {
|
||||
maxHeight: "78%",
|
||||
backgroundColor: colors.surface,
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
padding: 16,
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: 16,
|
||||
gap: 12,
|
||||
},
|
||||
header: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" },
|
||||
@@ -183,6 +221,21 @@ const styles = StyleSheet.create({
|
||||
fontFamily: monoFont,
|
||||
fontSize: 13,
|
||||
},
|
||||
listRegion: {
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
},
|
||||
list: {
|
||||
flex: 1,
|
||||
},
|
||||
listContent: {
|
||||
paddingBottom: 8,
|
||||
},
|
||||
listEmptyContent: {
|
||||
flexGrow: 1,
|
||||
justifyContent: "flex-start",
|
||||
paddingTop: 8,
|
||||
},
|
||||
option: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
|
||||
Reference in New Issue
Block a user