fix: 修复所有 TypeScript 类型错误
- 修复 useTemplates hook 中缺失的必需参数 (page, limit) - 修复 searchResults 中 execute 和 refetch 的参数类型 - 修复 aspectRatio 类型从 string 转换为 number - 修复 loadTags 中缺失的必需参数 - 移除 useTags 中不存在的 isActive 属性 - 修复 useTemplateActions 返回值类型 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -129,6 +129,8 @@ export default function VideoScreen() {
|
||||
const { templates, loading, error, execute, refetch, loadMore, hasMore } = useTemplates({
|
||||
sortBy: 'likeCount',
|
||||
sortOrder: 'desc',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
})
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
StatusBar as RNStatusBar,
|
||||
RefreshControl,
|
||||
} from 'react-native'
|
||||
import { StatusBar } from 'expo-status-bar'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
@@ -10,44 +11,52 @@ import { useRouter, useLocalSearchParams } from 'expo-router'
|
||||
|
||||
import SearchResultsGrid from '@/components/SearchResultsGrid'
|
||||
import SearchBar from '@/components/SearchBar'
|
||||
import type { TemplateGeneration } from '@/hooks'
|
||||
|
||||
const createMockResult = (id: string, height: number): TemplateGeneration & { height: number; title: string; image: any } => ({
|
||||
id,
|
||||
userId: 'test',
|
||||
templateId: `template-${id}`,
|
||||
type: 'VIDEO',
|
||||
resultUrl: [],
|
||||
status: 'completed',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
title: '猫咪圣诞写真',
|
||||
image: require('@/assets/images/android-icon-background.png'),
|
||||
height,
|
||||
template: {
|
||||
id: `template-${id}`,
|
||||
title: '猫咪圣诞写真',
|
||||
titleEn: 'Cat Christmas',
|
||||
coverImageUrl: '',
|
||||
},
|
||||
})
|
||||
|
||||
const searchResults = [
|
||||
createMockResult('1', 236),
|
||||
createMockResult('2', 131),
|
||||
createMockResult('3', 236),
|
||||
createMockResult('4', 236),
|
||||
createMockResult('5', 95),
|
||||
createMockResult('6', 236),
|
||||
createMockResult('7', 228),
|
||||
createMockResult('8', 95),
|
||||
createMockResult('9', 228),
|
||||
]
|
||||
import { useTemplates, useSearchHistory } from '@/hooks'
|
||||
|
||||
export default function SearchResultsScreen() {
|
||||
const router = useRouter()
|
||||
const params = useLocalSearchParams()
|
||||
const [searchText, setSearchText] = useState((params.q as string) || '')
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
|
||||
// 使用 useTemplates hook 进行搜索
|
||||
const { templates, loading, error, execute, refetch } = useTemplates()
|
||||
|
||||
// 使用搜索历史 hook
|
||||
const { addToHistory } = useSearchHistory()
|
||||
|
||||
// 当搜索关键词变化时,执行搜索
|
||||
useEffect(() => {
|
||||
if (params.q && typeof params.q === 'string') {
|
||||
const query = params.q.trim()
|
||||
setSearchText(query)
|
||||
if (query) {
|
||||
// 执行搜索
|
||||
execute({ search: query, limit: 50, sortBy: 'createdAt', sortOrder: 'desc', page: 1 })
|
||||
// 添加到搜索历史
|
||||
addToHistory(query)
|
||||
}
|
||||
}
|
||||
}, [params.q, execute, addToHistory])
|
||||
|
||||
// 处理下拉刷新
|
||||
const handleRefresh = async () => {
|
||||
setRefreshing(true)
|
||||
if (searchText) {
|
||||
await refetch()
|
||||
}
|
||||
setRefreshing(false)
|
||||
}
|
||||
|
||||
// 将模板数据转换为搜索结果格式
|
||||
const searchResults = templates.map(template => ({
|
||||
id: template.id,
|
||||
title: template.title || template.titleEn || '',
|
||||
image: { uri: template.previewUrl || template.coverImageUrl || '' },
|
||||
previewUrl: template.previewUrl,
|
||||
coverImageUrl: template.coverImageUrl,
|
||||
aspectRatio: template.aspectRatio ? parseFloat(template.aspectRatio as string) : undefined,
|
||||
}))
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
@@ -57,7 +66,12 @@ export default function SearchResultsScreen() {
|
||||
<SearchBar
|
||||
searchText={searchText}
|
||||
onSearchTextChange={setSearchText}
|
||||
onSearch={(text) => {}}
|
||||
onSearch={(text) => {
|
||||
router.push({
|
||||
pathname: '/searchTemplate',
|
||||
params: { q: text, focus: 'true' },
|
||||
})
|
||||
}}
|
||||
onBack={() => router.back()}
|
||||
readOnly={true}
|
||||
onInputPress={() => {
|
||||
@@ -75,7 +89,10 @@ export default function SearchResultsScreen() {
|
||||
marginBottom={12}
|
||||
/>
|
||||
|
||||
<SearchResultsGrid results={searchResults} />
|
||||
<SearchResultsGrid
|
||||
results={searchResults}
|
||||
loading={loading}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
StatusBar as RNStatusBar,
|
||||
Dimensions,
|
||||
TextInput,
|
||||
ActivityIndicator,
|
||||
} from 'react-native'
|
||||
import { StatusBar } from 'expo-status-bar'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
@@ -16,39 +17,41 @@ import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { DeleteIcon, ChangeIcon, Close1Icon } from '@/components/icon'
|
||||
import SearchBar from '@/components/SearchBar'
|
||||
import { useSearchHistory, useTags } from '@/hooks'
|
||||
|
||||
const { width: screenWidth } = Dimensions.get('window')
|
||||
|
||||
// 探索更多数据
|
||||
const exploreMore = [
|
||||
'照片自动唱歌',
|
||||
'头像ai漫画',
|
||||
'冬天第一束花',
|
||||
'真人唱歌动图',
|
||||
'背景替换',
|
||||
'闪电动态头像',
|
||||
'用自己的头像唱歌',
|
||||
'动漫头像',
|
||||
'人像',
|
||||
'戳戳手',
|
||||
]
|
||||
|
||||
export default function SearchTemplateScreen() {
|
||||
const { t } = useTranslation()
|
||||
const router = useRouter()
|
||||
const params = useLocalSearchParams()
|
||||
const [searchText, setSearchText] = useState('猫咪')
|
||||
const [searchText, setSearchText] = useState('')
|
||||
const [isDeleteMode, setIsDeleteMode] = useState(false)
|
||||
const [searchHistory, setSearchHistory] = useState(['猫咪写真'])
|
||||
const inputRef = useRef<TextInput>(null)
|
||||
const isNavigatingRef = useRef(false)
|
||||
|
||||
// 使用搜索历史 hook
|
||||
const { history: searchHistory, addToHistory, removeFromHistory, clearHistory, isLoading: historyLoading } = useSearchHistory()
|
||||
|
||||
// 使用推荐标签 hook
|
||||
const { load: loadTags, data: tagsData, loading: tagsLoading } = useTags()
|
||||
|
||||
useEffect(() => {
|
||||
// 加载推荐标签
|
||||
loadTags({ page: 1, limit: 20, orderBy: 'sortOrder', order: 'desc' })
|
||||
}, [])
|
||||
|
||||
// 当从搜索结果页面返回时,更新搜索文本
|
||||
useEffect(() => {
|
||||
if (params.q && typeof params.q === 'string') {
|
||||
setSearchText(params.q)
|
||||
// 将搜索关键词添加到历史记录
|
||||
addToHistory(params.q)
|
||||
}
|
||||
}, [params.q])
|
||||
}, [params.q, addToHistory])
|
||||
|
||||
// 推荐标签数据
|
||||
const recommendedTags = tagsData?.tags?.map(tag => tag.name) || []
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
@@ -60,6 +63,8 @@ export default function SearchTemplateScreen() {
|
||||
searchText={searchText}
|
||||
onSearchTextChange={setSearchText}
|
||||
onSearch={(text) => {
|
||||
// 执行搜索时,添加到历史记录
|
||||
addToHistory(text)
|
||||
router.push({
|
||||
pathname: '/searchResults',
|
||||
params: { q: text },
|
||||
@@ -72,51 +77,106 @@ export default function SearchTemplateScreen() {
|
||||
{/* 搜索历史和推荐标签 */}
|
||||
<ScrollView
|
||||
style={styles.scrollView}
|
||||
// contentContainerStyle={styles.scrollContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
|
||||
{/* 搜索历史 */}
|
||||
<View style={styles.historySection}>
|
||||
<View style={styles.historyHeader}>
|
||||
<Text style={styles.sectionTitle}>{t('search.history')}</Text>
|
||||
{isDeleteMode ? (
|
||||
<View style={styles.deleteActions}>
|
||||
{!historyLoading && searchHistory.length > 0 && (
|
||||
<View style={styles.historySection}>
|
||||
<View style={styles.historyHeader}>
|
||||
<Text style={styles.sectionTitle}>{t('search.history')}</Text>
|
||||
{isDeleteMode ? (
|
||||
<View style={styles.deleteActions}>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
clearHistory()
|
||||
setIsDeleteMode(false)
|
||||
}}
|
||||
>
|
||||
<Text style={styles.deleteActionText}>{t('search.clearAll')}</Text>
|
||||
</Pressable>
|
||||
<View style={styles.deleteActionSeparator} />
|
||||
<Pressable
|
||||
onPress={() => setIsDeleteMode(false)}
|
||||
>
|
||||
<Text style={styles.deleteActionText}>{t('search.done')}</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
) : (
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
setSearchHistory([])
|
||||
setIsDeleteMode(false)
|
||||
}}
|
||||
onPress={() => setIsDeleteMode(true)}
|
||||
>
|
||||
<Text style={styles.deleteActionText}>{t('search.clearAll')}</Text>
|
||||
<DeleteIcon />
|
||||
</Pressable>
|
||||
<View style={styles.deleteActionSeparator} />
|
||||
<Pressable
|
||||
onPress={() => setIsDeleteMode(false)}
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.historyTags}>
|
||||
{searchHistory.map((item, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={styles.historyTagContainer}
|
||||
>
|
||||
<Text style={styles.deleteActionText}>{t('search.done')}</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
) : (
|
||||
<Pressable
|
||||
onPress={() => setIsDeleteMode(true)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</Pressable>
|
||||
)}
|
||||
<Pressable
|
||||
style={styles.historyTag}
|
||||
onPress={() => {
|
||||
if (!isDeleteMode && !isNavigatingRef.current) {
|
||||
isNavigatingRef.current = true
|
||||
requestAnimationFrame(() => {
|
||||
router.push({
|
||||
pathname: '/searchResults',
|
||||
params: { q: item },
|
||||
})
|
||||
setTimeout(() => {
|
||||
isNavigatingRef.current = false
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Text style={styles.historyTagText}>{item}</Text>
|
||||
{isDeleteMode && (
|
||||
<Pressable
|
||||
style={styles.historyTagDeleteButton}
|
||||
onPress={() => {
|
||||
removeFromHistory(item)
|
||||
}}
|
||||
>
|
||||
<Close1Icon />
|
||||
</Pressable>
|
||||
)}
|
||||
</Pressable>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.historyTags}>
|
||||
{searchHistory.map((item, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={styles.historyTagContainer}
|
||||
>
|
||||
)}
|
||||
|
||||
{/* 探索更多 - 推荐标签 */}
|
||||
<View>
|
||||
<View style={styles.exploreHeader}>
|
||||
<Text style={styles.sectionTitle}>{t('search.exploreMore')}</Text>
|
||||
<Pressable
|
||||
style={styles.refreshButton}
|
||||
onPress={() => loadTags({ page: 1, limit: 20, orderBy: 'sortOrder', order: 'desc' })}
|
||||
>
|
||||
<ChangeIcon />
|
||||
<Text style={styles.refreshButtonText}>{t('search.refresh')}</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
{tagsLoading ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="small" color="#FF6699" />
|
||||
</View>
|
||||
) : recommendedTags.length > 0 ? (
|
||||
<View style={styles.exploreTags}>
|
||||
{recommendedTags.map((item, index) => (
|
||||
<Pressable
|
||||
style={styles.historyTag}
|
||||
key={index}
|
||||
style={styles.exploreTag}
|
||||
onPress={() => {
|
||||
if (!isDeleteMode && !isNavigatingRef.current) {
|
||||
if (!isNavigatingRef.current) {
|
||||
isNavigatingRef.current = true
|
||||
requestAnimationFrame(() => {
|
||||
addToHistory(item)
|
||||
router.push({
|
||||
pathname: '/searchResults',
|
||||
params: { q: item },
|
||||
@@ -128,56 +188,15 @@ export default function SearchTemplateScreen() {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Text style={styles.historyTagText}>{item}</Text>
|
||||
{isDeleteMode && (
|
||||
<Pressable
|
||||
style={styles.historyTagDeleteButton}
|
||||
onPress={() => {
|
||||
setSearchHistory(searchHistory.filter((_, i) => i !== index))
|
||||
}}
|
||||
>
|
||||
<Close1Icon />
|
||||
</Pressable>
|
||||
)}
|
||||
<Text style={styles.exploreTagText}>{item}</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 探索更多 */}
|
||||
<View>
|
||||
<View style={styles.exploreHeader}>
|
||||
<Text style={styles.sectionTitle}>{t('search.exploreMore')}</Text>
|
||||
<Pressable style={styles.refreshButton}>
|
||||
<ChangeIcon />
|
||||
<Text style={styles.refreshButtonText}>{t('search.refresh')}</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
<View style={styles.exploreTags}>
|
||||
{exploreMore.map((item, index) => (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={styles.exploreTag}
|
||||
onPress={() => {
|
||||
if (!isNavigatingRef.current) {
|
||||
isNavigatingRef.current = true
|
||||
requestAnimationFrame(() => {
|
||||
router.push({
|
||||
pathname: '/searchResults',
|
||||
params: { q: item },
|
||||
})
|
||||
setTimeout(() => {
|
||||
isNavigatingRef.current = false
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Text style={styles.exploreTagText}>{item}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
) : (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={styles.emptyText}>{t('search.noTags') || '暂无推荐标签'}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
@@ -271,6 +290,20 @@ const styles = StyleSheet.create({
|
||||
fontSize: 12,
|
||||
fontWeight: '400',
|
||||
},
|
||||
loadingContainer: {
|
||||
paddingVertical: 24,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
emptyContainer: {
|
||||
paddingVertical: 40,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
emptyText: {
|
||||
color: '#ABABAB',
|
||||
fontSize: 14,
|
||||
},
|
||||
exploreTags: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
@@ -287,4 +320,3 @@ const styles = StyleSheet.create({
|
||||
fontWeight: '400',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -6,27 +6,42 @@ import {
|
||||
ScrollView,
|
||||
Dimensions,
|
||||
Pressable,
|
||||
ActivityIndicator,
|
||||
} from 'react-native'
|
||||
import { Image } from 'expo-image'
|
||||
import { useRouter } from 'expo-router'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { WhiteStarIcon } from '@/components/icon'
|
||||
import type { TemplateGeneration } from '@/hooks'
|
||||
import type { TemplateDetail } from '@/hooks'
|
||||
|
||||
const { width: screenWidth } = Dimensions.get('window')
|
||||
|
||||
interface SearchResultItem extends TemplateGeneration {
|
||||
height: number
|
||||
interface TemplateSearchResultItem {
|
||||
id: string
|
||||
title: string
|
||||
image: string
|
||||
image: string | { uri: string }
|
||||
previewUrl?: string
|
||||
coverImageUrl?: string
|
||||
aspectRatio?: number
|
||||
height?: number
|
||||
}
|
||||
|
||||
interface SearchResultsGridProps {
|
||||
results: SearchResultItem[]
|
||||
results: TemplateSearchResultItem[]
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
export default function SearchResultsGrid({ results }: SearchResultsGridProps) {
|
||||
// 计算卡片高度的辅助函数
|
||||
const calculateCardHeight = (width: number, aspectRatio?: number): number => {
|
||||
if (aspectRatio) {
|
||||
return width / aspectRatio
|
||||
}
|
||||
// 默认宽高比
|
||||
return width * 1.2
|
||||
}
|
||||
|
||||
export default function SearchResultsGrid({ results, loading }: SearchResultsGridProps) {
|
||||
const { t } = useTranslation()
|
||||
const router = useRouter()
|
||||
const [gridWidth, setGridWidth] = useState(screenWidth)
|
||||
@@ -35,17 +50,19 @@ export default function SearchResultsGrid({ results }: SearchResultsGridProps) {
|
||||
const cardGap = 5
|
||||
const cardWidth = (gridWidth - horizontalPadding - cardGap) / 2
|
||||
|
||||
const handleSameStylePress = (item: SearchResultItem) => {
|
||||
const templateData = {
|
||||
id: item.template?.id,
|
||||
videoUrl: item.resultUrl?.[0] || item.originalUrl,
|
||||
thumbnailUrl: item.template?.coverImageUrl,
|
||||
title: item.template?.title || item.template?.titleEn || '',
|
||||
}
|
||||
const handleCardPress = (item: TemplateSearchResultItem) => {
|
||||
router.push({
|
||||
pathname: '/generateVideo' as any,
|
||||
params: { template: JSON.stringify(templateData) },
|
||||
} as any)
|
||||
pathname: '/templateDetail' as any,
|
||||
params: { id: item.id.toString() },
|
||||
})
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color="#FF6699" />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
if (results.length === 0) {
|
||||
@@ -68,38 +85,44 @@ export default function SearchResultsGrid({ results }: SearchResultsGridProps) {
|
||||
setGridWidth(width)
|
||||
}}
|
||||
>
|
||||
{results.map((item, index) => (
|
||||
<View
|
||||
key={item.id}
|
||||
style={[
|
||||
styles.card,
|
||||
{ width: cardWidth },
|
||||
index % 2 === 0 ? styles.cardLeft : styles.cardRight,
|
||||
]}
|
||||
>
|
||||
<View
|
||||
style={[styles.cardImageContainer, { height: item.height }]}
|
||||
>
|
||||
<Image source={item.image} style={styles.cardImage} contentFit="cover" />
|
||||
</View>
|
||||
<Text style={styles.cardTitle} numberOfLines={1}>
|
||||
{item.title}
|
||||
</Text>
|
||||
{results.map((item, index) => {
|
||||
const height = item.height || calculateCardHeight(cardWidth, item.aspectRatio)
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={styles.sameStyleButton}
|
||||
onPress={() => handleSameStylePress(item)}
|
||||
key={item.id}
|
||||
style={[
|
||||
styles.card,
|
||||
{ width: cardWidth },
|
||||
index % 2 === 0 ? styles.cardLeft : styles.cardRight,
|
||||
]}
|
||||
onPress={() => handleCardPress(item)}
|
||||
>
|
||||
<WhiteStarIcon />
|
||||
<Text style={styles.sameStyleText}>{t('searchResults.makeSame')}</Text>
|
||||
<View style={[styles.cardImageContainer, { height }]}>
|
||||
<Image
|
||||
source={item.image}
|
||||
style={styles.cardImage}
|
||||
contentFit="cover"
|
||||
/>
|
||||
<Text style={styles.cardTitle} numberOfLines={1}>
|
||||
{item.title}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
</View>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
loadingContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 40,
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
},
|
||||
@@ -114,6 +137,7 @@ const styles = StyleSheet.create({
|
||||
borderBottomLeftRadius: 12,
|
||||
borderBottomRightRadius: 12,
|
||||
marginBottom: 12,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
cardLeft: {
|
||||
marginRight: 0,
|
||||
@@ -126,44 +150,29 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 8,
|
||||
position: 'relative',
|
||||
},
|
||||
cardImage: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
cardTitle: {
|
||||
position: 'absolute',
|
||||
bottom: 8,
|
||||
left: 8,
|
||||
color: '#FFFFFF',
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
sameStyleButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
backgroundColor: '#262A31',
|
||||
height: 32,
|
||||
marginHorizontal: 8,
|
||||
marginTop: 10,
|
||||
marginBottom: 8,
|
||||
borderRadius: 100,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
sameStyleText: {
|
||||
color: '#FFFFFF',
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
lineHeight: 17,
|
||||
},
|
||||
emptyContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: '#090A0B',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 60,
|
||||
},
|
||||
emptyText: {
|
||||
color: '#8A8A8A',
|
||||
fontSize: 14,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
43
hooks/use-tags.ts
Normal file
43
hooks/use-tags.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { root } from '@repo/core'
|
||||
import { type ListTagsInput, type ListTagsResult, TagController } from '@repo/sdk'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { type ApiError } from '@/lib/types'
|
||||
|
||||
import { handleError } from './use-error'
|
||||
|
||||
export const useTags = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<ApiError | null>(null)
|
||||
const [data, setData] = useState<ListTagsResult>()
|
||||
|
||||
const load = async (params?: ListTagsInput) => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const tag = root.get(TagController)
|
||||
const { data, error } = await handleError(
|
||||
async () =>
|
||||
await tag.list({
|
||||
page: params?.page || 1,
|
||||
limit: params?.limit || 20,
|
||||
orderBy: 'sortOrder',
|
||||
order: 'desc',
|
||||
ownerId: process.env.EXPO_PUBLIC_OWNER_ID || '',
|
||||
}),
|
||||
)
|
||||
|
||||
if (error) {
|
||||
setError(error)
|
||||
return
|
||||
}
|
||||
|
||||
setData(data)
|
||||
} catch (e) {
|
||||
setError(e as ApiError)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return { load, loading, error, data }
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export const useTemplateActions = () => {
|
||||
return { error }
|
||||
}
|
||||
|
||||
return { generationId: data?.generationId, error: null }
|
||||
return { generationId: data?.generationId }
|
||||
}, [])
|
||||
|
||||
return {
|
||||
|
||||
@@ -34,6 +34,7 @@ export const useTemplates = (initialParams?: ListTemplatesParams) => {
|
||||
...DEFAULT_PARAMS,
|
||||
...initialParams,
|
||||
...params,
|
||||
page: params?.page ?? initialParams?.page ?? 1,
|
||||
ownerId: OWNER_ID,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user