Files
RSSuper/src/components/search-filters.tsx
2026-03-28 23:51:50 -04:00

217 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from 'react';
import { StyleSheet, ScrollView, TouchableOpacity, View } from 'react-native';
import { SearchFilters } from '@/types/feed';
import { ThemedText } from './themed-text';
import { ThemedView } from './themed-view';
import { Colors, Spacing } from '@/constants/theme';
import { useColorScheme } from 'react-native';
interface FilterChipProps {
label: string;
value?: string;
onRemove: () => void;
icon?: string;
}
function FilterChip({ label, value, onRemove, icon }: FilterChipProps) {
const scheme = useColorScheme();
const colors = Colors[scheme === 'unspecified' ? 'light' : scheme];
return (
<TouchableOpacity
style={styles.chip}
onPress={onRemove}
activeOpacity={0.7}
>
{icon && <ThemedText style={styles.chipIcon}>{icon}</ThemedText>}
<ThemedText style={styles.chipText}>
{label}:
</ThemedText>
<ThemedText style={styles.chipValue}>
{value}
</ThemedText>
<ThemedText style={styles.chipClose}></ThemedText>
</TouchableOpacity>
);
}
interface SearchFiltersProps {
filters: SearchFilters;
onFilterChange: (filters: Partial<SearchFilters>) => void;
onClearAll: () => void;
availableFeeds?: Array<{ id: string; title: string }>;
}
export function SearchFilters({ filters, onFilterChange, onClearAll, availableFeeds }: SearchFiltersProps) {
const scheme = useColorScheme();
const colors = Colors[scheme === 'unspecified' ? 'light' : scheme];
const getFeedTitle = (feedId: string) => {
const feed = availableFeeds?.find(f => f.id === feedId);
return feed?.title || feedId;
};
const formatDate = (date: Date) => {
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
};
const renderFilterChips = () => {
const chips: any[] = [];
// Date range filter
if (filters.dateFrom || filters.dateTo) {
const fromLabel = filters.dateFrom ? formatDate(filters.dateFrom) : 'Any date';
const toLabel = filters.dateTo ? formatDate(filters.dateTo) : 'Now';
chips.push(
<FilterChip
key="date"
label="Date"
value={`${fromLabel} ${toLabel}`}
icon="📅"
onRemove={() => onFilterChange({ dateFrom: undefined, dateTo: undefined })}
/>
);
}
// Feed filters
if (filters.feedIds && filters.feedIds.length > 0) {
const feedCount = filters.feedIds.length;
const feedTitle = feedCount === 1
? getFeedTitle(filters.feedIds[0])
: `${feedCount} feeds`;
chips.push(
<FilterChip
key="feeds"
label="Feed"
value={feedTitle}
icon="📰"
onRemove={() => onFilterChange({ feedIds: undefined })}
/>
);
}
// Author filters
if (filters.authors && filters.authors.length > 0) {
const authorCount = filters.authors.length;
const authorLabel = authorCount === 1
? filters.authors[0]
: `${authorCount} authors`;
chips.push(
<FilterChip
key="authors"
label="Author"
value={authorLabel}
icon="✍️"
onRemove={() => onFilterChange({ authors: undefined })}
/>
);
}
// Content type filter
if (filters.contentType) {
const contentTypeLabels: Record<string, string> = {
article: 'Articles',
audio: 'Audio',
video: 'Video',
};
const contentTypeIcons: Record<string, string> = {
article: '📄',
audio: '🎵',
video: '🎬',
};
chips.push(
<FilterChip
key="contentType"
label="Type"
value={contentTypeLabels[filters.contentType]}
icon={contentTypeIcons[filters.contentType]}
onRemove={() => onFilterChange({ contentType: undefined })}
/>
);
}
return chips;
};
const hasActiveFilters =
filters.dateFrom ||
filters.dateTo ||
(filters.feedIds?.length ?? 0) > 0 ||
(filters.authors?.length ?? 0) > 0 ||
filters.contentType !== undefined;
if (!hasActiveFilters) {
return null;
}
return (
<ThemedView style={styles.container}>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.chipsContainer}
>
{renderFilterChips()}
</ScrollView>
{hasActiveFilters && (
<TouchableOpacity
style={styles.clearAllButton}
onPress={onClearAll}
activeOpacity={0.7}
>
<ThemedText style={styles.clearAllText}>Clear all</ThemedText>
</TouchableOpacity>
)}
</ThemedView>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: Spacing.three,
paddingVertical: Spacing.two,
gap: Spacing.two,
},
chipsContainer: {
flex: 1,
gap: Spacing.two,
},
chip: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: Spacing.three,
paddingVertical: Spacing.one,
borderRadius: Spacing.two,
backgroundColor: '#e5e5ea',
gap: Spacing.one,
},
chipIcon: {
fontSize: 12,
},
chipText: {
fontSize: 12,
color: '#8e8e93',
fontWeight: '600',
},
chipValue: {
fontSize: 12,
color: '#000000',
},
chipClose: {
fontSize: 14,
color: '#8e8e93',
marginLeft: Spacing.one,
},
clearAllButton: {
paddingHorizontal: Spacing.two,
paddingVertical: Spacing.one,
},
clearAllText: {
fontSize: 12,
color: '#007AFF',
fontWeight: '500',
},
});