This commit is contained in:
2026-02-04 09:39:58 -05:00
parent bd4747679d
commit f7df578461
26 changed files with 907 additions and 783 deletions

View File

@@ -35,7 +35,7 @@ const TRENDING_PODCASTS: Podcast[] = [
feedUrl: "https://example.com/aitoday.rss",
author: "Tech Futures",
categories: ["Technology", "Science"],
imageUrl: undefined,
coverUrl: undefined,
lastUpdated: new Date(),
isSubscribed: false,
},

View File

@@ -4,7 +4,8 @@
*/
import { createSignal } from "solid-js"
import type { Feed, FeedFilter, FeedVisibility, FeedSortField } from "../types/feed"
import { FeedVisibility } from "../types/feed"
import type { Feed, FeedFilter, FeedSortField } from "../types/feed"
import type { Podcast } from "../types/podcast"
import type { Episode, EpisodeStatus } from "../types/episode"
import type { PodcastSource, SourceType } from "../types/source"
@@ -287,7 +288,7 @@ export function createFeedStore() {
}
/** Add a new feed */
const addFeed = (podcast: Podcast, sourceId: string, visibility: FeedVisibility = "public") => {
const addFeed = (podcast: Podcast, sourceId: string, visibility: FeedVisibility = FeedVisibility.PUBLIC) => {
const newFeed: Feed = {
id: crypto.randomUUID(),
podcast,

View File

@@ -4,8 +4,9 @@
*/
import { createSignal } from "solid-js"
import type { Podcast } from "../types/podcast"
import type { PodcastSource, SearchResult } from "../types/source"
import { searchPodcasts } from "../utils/search"
import { useFeedStore } from "./feed"
import type { SearchResult } from "../types/source"
const STORAGE_KEY = "podtui_search_history"
const MAX_HISTORY = 20
@@ -17,89 +18,7 @@ export interface SearchState {
error: string | null
}
/** Mock search results for demonstration */
const MOCK_PODCASTS: Podcast[] = [
{
id: "search-1",
title: "Tech Talk Daily",
description: "Daily technology news and analysis from Silicon Valley experts.",
feedUrl: "https://example.com/techtalk.rss",
author: "Tech Media Group",
categories: ["Technology", "News"],
lastUpdated: new Date(),
isSubscribed: false,
},
{
id: "search-2",
title: "The Science Hour",
description: "Weekly deep dives into the latest scientific discoveries and research.",
feedUrl: "https://example.com/sciencehour.rss",
author: "Science Network",
categories: ["Science", "Education"],
lastUpdated: new Date(),
isSubscribed: false,
},
{
id: "search-3",
title: "History Lessons",
description: "Fascinating stories from history that shaped our world.",
feedUrl: "https://example.com/historylessons.rss",
author: "History Channel",
categories: ["History", "Education"],
lastUpdated: new Date(),
isSubscribed: false,
},
{
id: "search-4",
title: "Business Insights",
description: "Expert analysis on business trends, markets, and entrepreneurship.",
feedUrl: "https://example.com/businessinsights.rss",
author: "Business Weekly",
categories: ["Business", "Finance"],
lastUpdated: new Date(),
isSubscribed: false,
},
{
id: "search-5",
title: "True Crime Stories",
description: "In-depth investigations into real criminal cases and mysteries.",
feedUrl: "https://example.com/truecrime.rss",
author: "Crime Network",
categories: ["True Crime", "Documentary"],
lastUpdated: new Date(),
isSubscribed: false,
},
{
id: "search-6",
title: "Comedy Hour",
description: "Stand-up comedy, sketches, and hilarious conversations.",
feedUrl: "https://example.com/comedyhour.rss",
author: "Laugh Factory",
categories: ["Comedy", "Entertainment"],
lastUpdated: new Date(),
isSubscribed: false,
},
{
id: "search-7",
title: "Mindful Living",
description: "Meditation, wellness, and mental health tips for a better life.",
feedUrl: "https://example.com/mindful.rss",
author: "Wellness Media",
categories: ["Health", "Self-Help"],
lastUpdated: new Date(),
isSubscribed: false,
},
{
id: "search-8",
title: "Sports Central",
description: "Coverage of all major sports, analysis, and athlete interviews.",
feedUrl: "https://example.com/sportscentral.rss",
author: "Sports Network",
categories: ["Sports", "News"],
lastUpdated: new Date(),
isSubscribed: false,
},
]
const CACHE_TTL = 1000 * 60 * 5
/** Load search history from localStorage */
function loadHistory(): string[] {
@@ -124,6 +43,7 @@ function saveHistory(history: string[]): void {
/** Create search store */
export function createSearchStore() {
const feedStore = useFeedStore()
const [query, setQuery] = createSignal("")
const [isSearching, setIsSearching] = createSignal(false)
const [results, setResults] = createSignal<SearchResult[]>([])
@@ -131,7 +51,24 @@ export function createSearchStore() {
const [history, setHistory] = createSignal<string[]>(loadHistory())
const [selectedSources, setSelectedSources] = createSignal<string[]>([])
/** Perform search (mock implementation) */
const applySubscribedStatus = (items: SearchResult[]): SearchResult[] => {
const feeds = feedStore.feeds()
const subscribedUrls = new Set(feeds.map((feed) => feed.podcast.feedUrl))
const subscribedIds = new Set(feeds.map((feed) => feed.podcast.id))
return items.map((item) => ({
...item,
podcast: {
...item.podcast,
isSubscribed:
item.podcast.isSubscribed ||
subscribedUrls.has(item.podcast.feedUrl) ||
subscribedIds.has(item.podcast.id),
},
}))
}
/** Perform search (multi-source implementation) */
const search = async (searchQuery: string): Promise<void> => {
const q = searchQuery.trim()
if (!q) {
@@ -146,28 +83,18 @@ export function createSearchStore() {
// Add to history
addToHistory(q)
// Simulate network delay
await new Promise((r) => setTimeout(r, 300 + Math.random() * 500))
try {
// Mock search - filter by query
const queryLower = q.toLowerCase()
const matchingPodcasts = MOCK_PODCASTS.filter(
(p) =>
p.title.toLowerCase().includes(queryLower) ||
p.description.toLowerCase().includes(queryLower) ||
p.categories?.some((c) => c.toLowerCase().includes(queryLower)) ||
p.author?.toLowerCase().includes(queryLower)
)
const sources = feedStore.sources()
const enabledSourceIds = sources.filter((s) => s.enabled).map((s) => s.id)
const sourceIds = selectedSources().length > 0
? selectedSources()
: enabledSourceIds
// Convert to search results
const searchResults: SearchResult[] = matchingPodcasts.map((podcast, i) => ({
sourceId: i % 2 === 0 ? "itunes" : "rss",
podcast,
score: 1 - i * 0.1, // Mock relevance score
}))
const searchResults = await searchPodcasts(q, sourceIds, sources, {
cacheTtl: CACHE_TTL,
})
setResults(searchResults)
setResults(applySubscribedStatus(searchResults))
} catch (e) {
setError("Search failed. Please try again.")
setResults([])
@@ -209,6 +136,26 @@ export function createSearchStore() {
setError(null)
}
/** Mark a podcast as subscribed in results */
const markSubscribed = (podcastId: string, feedUrl?: string) => {
setResults((prev) =>
prev.map((result) => {
const matchesId = result.podcast.id === podcastId
const matchesUrl = feedUrl ? result.podcast.feedUrl === feedUrl : false
if (matchesId || matchesUrl) {
return {
...result,
podcast: {
...result.podcast,
isSubscribed: true,
},
}
}
return result
})
)
}
return {
// State
query,
@@ -225,6 +172,7 @@ export function createSearchStore() {
clearHistory,
removeFromHistory,
setSelectedSources,
markSubscribed,
}
}