356 lines
11 KiB
TypeScript
356 lines
11 KiB
TypeScript
import { useState, useEffect, useRef, useCallback } from 'react'
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
TextInput,
|
|
ScrollView,
|
|
Pressable,
|
|
StatusBar as RNStatusBar,
|
|
Dimensions,
|
|
} from 'react-native'
|
|
import { StatusBar } from 'expo-status-bar'
|
|
import { SafeAreaView } from 'react-native-safe-area-context'
|
|
import { useRouter, useLocalSearchParams, useFocusEffect } from 'expo-router'
|
|
|
|
import { LeftArrowIcon, CloseIcon, DeleteIcon, ChangeIcon, Close1Icon } from '@/components/icon'
|
|
import { useTags, useSearchHistory } from '@/hooks/data'
|
|
|
|
const { width: screenWidth } = Dimensions.get('window')
|
|
|
|
export default function SearchTemplateScreen() {
|
|
const router = useRouter()
|
|
const params = useLocalSearchParams()
|
|
const [searchText, setSearchText] = useState('')
|
|
const [isDeleteMode, setIsDeleteMode] = useState(false)
|
|
const inputRef = useRef<TextInput>(null)
|
|
const { load, data: tagsData } = useTags()
|
|
const { history: searchHistory, addToHistory, removeFromHistory, clearHistory } = useSearchHistory()
|
|
|
|
useEffect(() => {
|
|
if (params.q && typeof params.q === 'string') {
|
|
setSearchText(params.q)
|
|
}
|
|
}, [params.q])
|
|
|
|
useEffect(() => {
|
|
load({ limit: 12 })
|
|
}, [])
|
|
|
|
// 当页面获得焦点且需要聚焦时,自动聚焦输入框
|
|
useFocusEffect(
|
|
useCallback(() => {
|
|
if (params.focus === 'true') {
|
|
// 延迟聚焦,确保页面渲染完成
|
|
const timer = setTimeout(() => {
|
|
inputRef.current?.focus()
|
|
}, 100)
|
|
return () => clearTimeout(timer)
|
|
}
|
|
}, [params.focus])
|
|
)
|
|
|
|
return (
|
|
<SafeAreaView style={styles.container} edges={['top']}>
|
|
<StatusBar style="light" />
|
|
<RNStatusBar barStyle="light-content" />
|
|
|
|
{/* Top Bar with Search */}
|
|
<View style={styles.topBar}>
|
|
<Pressable
|
|
onPress={() => router.push('/(tabs)')}
|
|
>
|
|
<LeftArrowIcon />
|
|
</Pressable>
|
|
|
|
<View style={styles.searchInputContainer}>
|
|
<TextInput
|
|
ref={inputRef}
|
|
style={styles.searchInput}
|
|
value={searchText}
|
|
onChangeText={setSearchText}
|
|
placeholder="搜索"
|
|
placeholderTextColor="#ABABAB"
|
|
underlineColorAndroid="transparent"
|
|
selectionColor="#F5F5F5"
|
|
autoCorrect={false}
|
|
autoCapitalize="none"
|
|
/>
|
|
{searchText.length > 0 && (
|
|
<Pressable
|
|
onPress={() => setSearchText('')}
|
|
style={styles.clearButton}
|
|
>
|
|
<CloseIcon />
|
|
</Pressable>
|
|
)}
|
|
</View>
|
|
<Pressable
|
|
style={styles.searchButton}
|
|
onPress={async () => {
|
|
if (searchText.trim()) {
|
|
await addToHistory(searchText.trim())
|
|
router.push({
|
|
pathname: '/searchResults',
|
|
params: { q: searchText.trim() },
|
|
})
|
|
}
|
|
}}
|
|
>
|
|
<Text style={styles.searchButtonText}>搜索</Text>
|
|
</Pressable>
|
|
</View>
|
|
{/* 搜索历史和推荐标签 */}
|
|
<ScrollView
|
|
style={styles.scrollView}
|
|
// contentContainerStyle={styles.scrollContent}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
|
|
{/* 搜索历史 */}
|
|
<View style={styles.historySection}>
|
|
<View style={styles.historyHeader}>
|
|
<Text style={styles.sectionTitle}>搜索历史</Text>
|
|
{isDeleteMode ? (
|
|
<View style={styles.deleteActions}>
|
|
<Pressable
|
|
onPress={async () => {
|
|
await clearHistory()
|
|
setIsDeleteMode(false)
|
|
}}
|
|
>
|
|
<Text style={styles.deleteActionText}>全部清除</Text>
|
|
</Pressable>
|
|
<View style={styles.deleteActionSeparator} />
|
|
<Pressable
|
|
onPress={() => setIsDeleteMode(false)}
|
|
>
|
|
<Text style={styles.deleteActionText}>完成</Text>
|
|
</Pressable>
|
|
</View>
|
|
) : (
|
|
<Pressable
|
|
onPress={() => setIsDeleteMode(true)}
|
|
>
|
|
<DeleteIcon />
|
|
</Pressable>
|
|
)}
|
|
</View>
|
|
<View style={styles.historyTags}>
|
|
{searchHistory.map((item, index) => (
|
|
<View
|
|
key={index}
|
|
style={styles.historyTagContainer}
|
|
>
|
|
<Pressable
|
|
style={styles.historyTag}
|
|
onPress={() => {
|
|
if (!isDeleteMode) {
|
|
setSearchText(item)
|
|
}
|
|
}}
|
|
>
|
|
<Text style={styles.historyTagText}>{item}</Text>
|
|
{isDeleteMode && (
|
|
<Pressable
|
|
style={styles.historyTagDeleteButton}
|
|
onPress={async () => {
|
|
await removeFromHistory(item)
|
|
}}
|
|
>
|
|
<Close1Icon />
|
|
</Pressable>
|
|
)}
|
|
</Pressable>
|
|
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
|
|
{/* 探索更多 */}
|
|
<View>
|
|
<View style={styles.exploreHeader}>
|
|
<Text style={styles.sectionTitle}>探索更多</Text>
|
|
<Pressable
|
|
style={styles.refreshButton}
|
|
onPress={() => load({ limit: 12 })}
|
|
>
|
|
<ChangeIcon />
|
|
<Text style={styles.refreshButtonText}>换一换</Text>
|
|
</Pressable>
|
|
</View>
|
|
<View style={styles.exploreTags}>
|
|
{tagsData?.tags.map((tag) => (
|
|
<Pressable
|
|
key={tag.id}
|
|
style={styles.exploreTag}
|
|
onPress={() => setSearchText(tag.name)}
|
|
>
|
|
<Text style={styles.exploreTagText}>{tag.name}</Text>
|
|
</Pressable>
|
|
))}
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: '#090A0B',
|
|
},
|
|
topBar: {
|
|
height: 44,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingHorizontal: 12,
|
|
marginTop:8,
|
|
marginBottom: 20,
|
|
},
|
|
scrollView: {
|
|
flex: 1,
|
|
paddingHorizontal: 12,
|
|
},
|
|
searchInputContainer: {
|
|
flex: 1,
|
|
marginLeft: 4,
|
|
marginRight: 12,
|
|
position: 'relative',
|
|
height: 40,
|
|
},
|
|
searchInput: {
|
|
flex: 1,
|
|
color: '#F5F5F5',
|
|
fontSize: 14,
|
|
height: 40,
|
|
paddingVertical: 8,
|
|
paddingLeft: 12,
|
|
paddingRight: 36,
|
|
borderRadius: 100,
|
|
backgroundColor: '#16181B',
|
|
},
|
|
clearButton: {
|
|
position: 'absolute',
|
|
right: 8,
|
|
top: 0,
|
|
bottom: 0,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
padding: 4,
|
|
},
|
|
searchButton: {
|
|
paddingHorizontal: 0,
|
|
paddingVertical: 8,
|
|
},
|
|
searchButtonText: {
|
|
color: '#F5F5F5',
|
|
fontSize: 14,
|
|
fontWeight: '400',
|
|
},
|
|
historySection: {
|
|
paddingBottom:32,
|
|
},
|
|
historyHeader: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
sectionTitle: {
|
|
color: '#F5F5F5',
|
|
fontSize: 14,
|
|
fontWeight: '500',
|
|
lineHeight: 20,
|
|
},
|
|
historyTags: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
gap: 4,
|
|
marginTop: 16,
|
|
},
|
|
historyTagContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 4,
|
|
},
|
|
historyTag: {
|
|
backgroundColor: '#191B1F',
|
|
borderRadius: 100,
|
|
paddingHorizontal: 11,
|
|
paddingVertical: 6,
|
|
borderWidth: 1,
|
|
borderColor: '#191B1F',
|
|
height: 29,
|
|
justifyContent: 'center',
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 4,
|
|
},
|
|
historyTagText: {
|
|
color: '#F5F5F5',
|
|
fontSize: 12,
|
|
},
|
|
historyTagDeleteButton: {
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
deleteActions: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
deleteActionText: {
|
|
color: '#ABABAB',
|
|
fontSize: 14,
|
|
fontWeight: '400',
|
|
},
|
|
deleteActionSeparator: {
|
|
width: 1,
|
|
height: 14,
|
|
backgroundColor: '#ABABAB',
|
|
marginHorizontal: 12,
|
|
},
|
|
exploreHeader: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginBottom: 16,
|
|
},
|
|
refreshButton: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 1,
|
|
paddingVertical: 4,
|
|
},
|
|
refreshButtonText: {
|
|
color: '#8A8A8A',
|
|
fontSize: 12,
|
|
fontWeight: '400',
|
|
},
|
|
refreshIcon: {
|
|
width: 18,
|
|
height: 18,
|
|
borderRadius: 9,
|
|
backgroundColor: '#1C1E22',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
exploreTags: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
gap: 8,
|
|
},
|
|
exploreTag: {
|
|
width: (screenWidth - 24 - 8) / 2, // 屏幕宽度 - 左右padding(12*2) - gap(8) 除以2
|
|
justifyContent: 'center',
|
|
paddingVertical: 2,
|
|
},
|
|
exploreTagText: {
|
|
color: '#F5F5F5',
|
|
fontSize: 14,
|
|
fontWeight: '400',
|
|
},
|
|
})
|
|
|