mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-23 16:05:24 +00:00
Added search to feeds
This commit is contained in:
parent
185c96bf9b
commit
fd7b7567fe
@ -6,8 +6,9 @@ import { Button } from 'primereact/button';
|
|||||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||||
import { useDiscordQuery } from '@/hooks/communityQueries/useDiscordQuery';
|
import { useDiscordQuery } from '@/hooks/communityQueries/useDiscordQuery';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
import { highlightText } from '@/utils/text';
|
||||||
|
|
||||||
const DiscordFeed = () => {
|
const DiscordFeed = ({ searchQuery }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { data, error, isLoading } = useDiscordQuery({page: router.query.page});
|
const { data, error, isLoading } = useDiscordQuery({page: router.query.page});
|
||||||
|
|
||||||
@ -54,18 +55,22 @@ const DiscordFeed = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const filteredData = data.filter(message =>
|
||||||
|
message.content.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
||||||
<div className="mx-4 mt-4">
|
<div className="mx-4 mt-4">
|
||||||
{data && data.length > 0 ? (
|
{filteredData && filteredData.length > 0 ? (
|
||||||
data.map(message => (
|
filteredData.map(message => (
|
||||||
<Card
|
<Card
|
||||||
key={message.id}
|
key={message.id}
|
||||||
header={() => header(message)}
|
header={() => header(message)}
|
||||||
footer={() => footer(message)}
|
footer={() => footer(message)}
|
||||||
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
||||||
>
|
>
|
||||||
<p className="m-0 text-lg text-gray-200">{message.content}</p>
|
<p className="m-0 text-lg text-gray-200">{highlightText(message.content, searchQuery)}</p>
|
||||||
</Card>
|
</Card>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
@ -14,6 +14,7 @@ import { findKind0Fields } from '@/utils/nostr';
|
|||||||
import NostrIcon from '../../../public/images/nostr.png';
|
import NostrIcon from '../../../public/images/nostr.png';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useImageProxy } from '@/hooks/useImageProxy';
|
import { useImageProxy } from '@/hooks/useImageProxy';
|
||||||
|
import { highlightText } from '@/utils/text';
|
||||||
import { nip19 } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
|
|
||||||
const StackerNewsIconComponent = () => (
|
const StackerNewsIconComponent = () => (
|
||||||
@ -28,7 +29,7 @@ const fetchStackerNews = async () => {
|
|||||||
return response.data.data.items.items;
|
return response.data.data.items.items;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GlobalFeed = () => {
|
const GlobalFeed = ({searchQuery}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { data: discordData, error: discordError, isLoading: discordLoading } = useDiscordQuery({page: router.query.page});
|
const { data: discordData, error: discordError, isLoading: discordLoading } = useDiscordQuery({page: router.query.page});
|
||||||
const { data: stackerNewsData, error: stackerNewsError, isLoading: stackerNewsLoading } = useQuery({queryKey: ['stackerNews'], queryFn: fetchStackerNews});
|
const { data: stackerNewsData, error: stackerNewsError, isLoading: stackerNewsLoading } = useQuery({queryKey: ['stackerNews'], queryFn: fetchStackerNews});
|
||||||
@ -109,6 +110,14 @@ const GlobalFeed = () => {
|
|||||||
const dateA = a.type === 'nostr' ? a.created_at * 1000 : new Date(a.timestamp || a.createdAt);
|
const dateA = a.type === 'nostr' ? a.created_at * 1000 : new Date(a.timestamp || a.createdAt);
|
||||||
const dateB = b.type === 'nostr' ? b.created_at * 1000 : new Date(b.timestamp || b.createdAt);
|
const dateB = b.type === 'nostr' ? b.created_at * 1000 : new Date(b.timestamp || b.createdAt);
|
||||||
return dateB - dateA;
|
return dateB - dateA;
|
||||||
|
}).filter(item => {
|
||||||
|
const searchLower = searchQuery.toLowerCase();
|
||||||
|
if (item.type === 'discord' || item.type === 'nostr') {
|
||||||
|
return item.content.toLowerCase().includes(searchLower);
|
||||||
|
} else if (item.type === 'stackernews') {
|
||||||
|
return item.title.toLowerCase().includes(searchLower);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const header = (item) => (
|
const header = (item) => (
|
||||||
@ -192,10 +201,14 @@ const GlobalFeed = () => {
|
|||||||
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
||||||
>
|
>
|
||||||
{item.type === 'discord' || item.type === 'nostr' ? (
|
{item.type === 'discord' || item.type === 'nostr' ? (
|
||||||
<p className="m-0 text-lg text-gray-200 overflow-hidden break-words">{item.content}</p>
|
<p className="m-0 text-lg text-gray-200 overflow-hidden break-words">
|
||||||
|
{highlightText(item.content, searchQuery)}
|
||||||
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<h3 className="m-0 text-lg text-gray-200">{item.title}</h3>
|
<h3 className="m-0 text-lg text-gray-200">
|
||||||
|
{highlightText(item.title, searchQuery)}
|
||||||
|
</h3>
|
||||||
<p className="text-sm text-gray-400">
|
<p className="text-sm text-gray-400">
|
||||||
Comments: {item.comments.length} | Sats: {item.sats}
|
Comments: {item.comments.length} | Sats: {item.sats}
|
||||||
</p>
|
</p>
|
||||||
@ -204,7 +217,9 @@ const GlobalFeed = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="text-gray-400 text-center p-4">No items available.</div>
|
<div className="text-gray-400 text-center p-4">
|
||||||
|
{searchQuery ? "No matching items found." : "No items available."}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,9 +12,10 @@ import Image from 'next/image';
|
|||||||
import { useImageProxy } from '@/hooks/useImageProxy';
|
import { useImageProxy } from '@/hooks/useImageProxy';
|
||||||
import { nip19 } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
import { useCommunityNotes } from '@/hooks/nostr/useCommunityNotes';
|
import { useCommunityNotes } from '@/hooks/nostr/useCommunityNotes';
|
||||||
|
import { highlightText } from '@/utils/text';
|
||||||
import ZapThreadsWrapper from '@/components/ZapThreadsWrapper';
|
import ZapThreadsWrapper from '@/components/ZapThreadsWrapper';
|
||||||
|
|
||||||
const NostrFeed = () => {
|
const NostrFeed = ({ searchQuery }) => {
|
||||||
const { communityNotes, isLoading, error } = useCommunityNotes();
|
const { communityNotes, isLoading, error } = useCommunityNotes();
|
||||||
const { ndk } = useNDKContext();
|
const { ndk } = useNDKContext();
|
||||||
const { returnImageProxy } = useImageProxy();
|
const { returnImageProxy } = useImageProxy();
|
||||||
@ -134,18 +135,24 @@ const NostrFeed = () => {
|
|||||||
return <div className="text-red-500 text-center p-4">Failed to load messages. Please try again later.</div>;
|
return <div className="text-red-500 text-center p-4">Failed to load messages. Please try again later.</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filteredNotes = communityNotes.filter(message =>
|
||||||
|
message.content.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
||||||
<div className="mx-4 mt-4">
|
<div className="mx-4 mt-4">
|
||||||
{communityNotes.length > 0 ? (
|
{filteredNotes.length > 0 ? (
|
||||||
communityNotes.map(message => (
|
filteredNotes.map(message => (
|
||||||
<Card
|
<Card
|
||||||
key={message.id}
|
key={message.id}
|
||||||
header={renderHeader(message)}
|
header={renderHeader(message)}
|
||||||
footer={() => footer(message)}
|
footer={() => footer(message)}
|
||||||
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
||||||
>
|
>
|
||||||
<p className="m-0 text-lg text-gray-200 overflow-hidden break-words">{message.content}</p>
|
<p className="m-0 text-lg text-gray-200 overflow-hidden break-words">
|
||||||
|
{highlightText(message.content, searchQuery)}
|
||||||
|
</p>
|
||||||
</Card>
|
</Card>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
@ -5,6 +5,7 @@ import { Tag } from 'primereact/tag';
|
|||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { highlightText } from '@/utils/text';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const StackerNewsIconComponent = () => (
|
const StackerNewsIconComponent = () => (
|
||||||
@ -19,7 +20,7 @@ const fetchStackerNews = async () => {
|
|||||||
return response.data.data.items.items; // Note the change here
|
return response.data.data.items.items; // Note the change here
|
||||||
};
|
};
|
||||||
|
|
||||||
const StackerNewsFeed = () => {
|
const StackerNewsFeed = ({ searchQuery }) => {
|
||||||
const { data: items, isLoading, error } = useQuery({queryKey: ['stackerNews'], queryFn: fetchStackerNews});
|
const { data: items, isLoading, error } = useQuery({queryKey: ['stackerNews'], queryFn: fetchStackerNews});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -71,18 +72,22 @@ const StackerNewsFeed = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const filteredItems = items.filter(item =>
|
||||||
|
item.title.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
<div className="bg-gray-900 h-full w-full min-bottom-bar:w-[86vw]">
|
||||||
<div className="mx-4 mt-4">
|
<div className="mx-4 mt-4">
|
||||||
{items && items.length > 0 ? (
|
{filteredItems && filteredItems.length > 0 ? (
|
||||||
items.map(item => (
|
filteredItems.map(item => (
|
||||||
<Card
|
<Card
|
||||||
key={item.id}
|
key={item.id}
|
||||||
header={() => header(item)}
|
header={() => header(item)}
|
||||||
footer={() => footer(item)}
|
footer={() => footer(item)}
|
||||||
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
className="w-full bg-gray-700 shadow-lg hover:shadow-xl transition-shadow duration-300 mb-4"
|
||||||
>
|
>
|
||||||
<h3 className="m-0 text-lg text-gray-200">{item.title}</h3>
|
<h3 className="m-0 text-lg text-gray-200">{highlightText(item.title, searchQuery)}</h3>
|
||||||
<p className="text-sm text-gray-400">
|
<p className="text-sm text-gray-400">
|
||||||
Comments: {item.comments.length} | Sats: {item.sats}
|
Comments: {item.comments.length} | Sats: {item.sats}
|
||||||
</p>
|
</p>
|
||||||
|
@ -15,9 +15,9 @@ import { Divider } from 'primereact/divider';
|
|||||||
|
|
||||||
const Feed = () => {
|
const Feed = () => {
|
||||||
const [selectedTopic, setSelectedTopic] = useState('global');
|
const [selectedTopic, setSelectedTopic] = useState('global');
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
|
||||||
const [title, setTitle] = useState('Community');
|
const [title, setTitle] = useState('Community');
|
||||||
const allTopics = ['global', 'nostr', 'discord', 'stackernews'];
|
const allTopics = ['global', 'nostr', 'discord', 'stackernews'];
|
||||||
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -96,16 +96,16 @@ const Feed = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
selectedTopic === 'global' && <GlobalFeed />
|
selectedTopic === 'global' && <GlobalFeed searchQuery={searchQuery} />
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
selectedTopic === 'nostr' && <NostrFeed />
|
selectedTopic === 'nostr' && <NostrFeed searchQuery={searchQuery} />
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
selectedTopic === 'discord' && <DiscordFeed />
|
selectedTopic === 'discord' && <DiscordFeed searchQuery={searchQuery} />
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
selectedTopic === 'stackernews' && <StackerNewsFeed />
|
selectedTopic === 'stackernews' && <StackerNewsFeed searchQuery={searchQuery} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
9
src/utils/text.js
Normal file
9
src/utils/text.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export const highlightText = (text, query) => {
|
||||||
|
if (!query) return text;
|
||||||
|
const parts = text.split(new RegExp(`(${query})`, 'gi'));
|
||||||
|
return parts.map((part, index) =>
|
||||||
|
part.toLowerCase() === query.toLowerCase()
|
||||||
|
? <span key={index} className="bg-yellow-300 text-black">{part}</span>
|
||||||
|
: part
|
||||||
|
);
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user