From 5c3922f23071f5b76dc46dda32aebc25e0bbd420 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Thu, 30 Jul 2026 12:04:26 +0200 Subject: [PATCH] Revert "add Currency setting" This reverts commit 407f07c045323998df7c1941afac1d8eaf21fb83. --- src/App.tsx | 45 ++----- src/context/CurrencyContext.tsx | 125 ------------------- src/pages/AuthorProfilePage.tsx | 19 +-- src/pages/Home.tsx | 63 ++-------- src/pages/PluginDetail.tsx | 65 ++-------- src/pages/ProfilePage.tsx | 47 +------ src/pages/SearchResultsPage.tsx | 49 +++----- src/pages/dashboard/AnalyticsContext.tsx | 19 --- src/pages/dashboard/DashboardAudience.tsx | 4 +- src/pages/dashboard/DashboardOverview.tsx | 4 +- src/pages/dashboard/DashboardPlugins.tsx | 4 +- src/pages/dashboard/plugin/PublishUpdate.tsx | 5 +- 12 files changed, 64 insertions(+), 385 deletions(-) delete mode 100644 src/context/CurrencyContext.tsx delete mode 100644 src/pages/dashboard/AnalyticsContext.tsx diff --git a/src/App.tsx b/src/App.tsx index ce33157..cb628ab 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,7 +14,6 @@ import ProfilePage from './pages/ProfilePage'; import AuthorProfilePage from './pages/AuthorProfilePage'; import SearchResultsPage from './pages/SearchResultsPage'; import DashboardAudience from './pages/dashboard/DashboardAudience'; -import DashboardOverview from './pages/dashboard/DashboardOverview'; import DashboardPlugins from './pages/dashboard/DashboardPlugins'; import ManagePlugin from './pages/dashboard/plugin/ManagePlugin'; import AddPlugin from './pages/dashboard/plugin/AddPlugin'; @@ -25,9 +24,6 @@ import AdminPanel from './pages/admin/AdminPanel'; import './App.css'; import api from './api'; -import { AnalyticsContext } from './pages/dashboard/AnalyticsContext'; -import { useAnalytics } from './pages/dashboard/useAnalytics'; -import { CurrencyProvider } from './context/CurrencyContext'; // --- Scroll To Top Helper --- const ScrollToTop = () => { @@ -98,7 +94,7 @@ const AuthProvider = ({ children }: { children: React.ReactNode }) => { return ( - {!loading && {children}} + {!loading && children} ); }; @@ -144,8 +140,7 @@ const App = () => ( } /> } /> }> - } /> - } /> + } /> } /> } /> } /> @@ -153,7 +148,6 @@ const App = () => ( } /> } /> - } /> @@ -174,23 +168,15 @@ const MainLayout = () => { import './pages/dashboard/Dashboard.css'; const DashboardLayout = () => { - // Instantiate analytics once here and share via context — prevents each tab - // from making its own redundant API call. - const analytics = useAnalytics(); - return ( - -
- -
- -
+
+ +
+
- +
); }; @@ -233,7 +219,7 @@ const Navbar = ({ user }: any) => {
setIsMenuOpen(false)}>{t('home')} - {user && (user.is_developer || user.plugin_count > 0 || user.role === 'admin' || user.role === 'moderator') && ( + {user && (user.plugin_count > 0 || user.role === 'admin' || user.role === 'moderator') && ( setIsMenuOpen(false)}>{t('dashboard')} )} {user && setIsMenuOpen(false)}>Publish} @@ -330,15 +316,4 @@ const Footer = () => ( ); -const NotFoundPage = () => ( -
-
404
-

Page Not Found

-

- The page you're looking for doesn't exist or has been moved. -

- Go Home -
-); - export default App; \ No newline at end of file diff --git a/src/context/CurrencyContext.tsx b/src/context/CurrencyContext.tsx deleted file mode 100644 index fe61fbb..0000000 --- a/src/context/CurrencyContext.tsx +++ /dev/null @@ -1,125 +0,0 @@ -/** - * CurrencyContext - * - * Provides `currency`, `setCurrency`, and `formatPrice(cents)` throughout the app. - * The preferred currency is read from the JWT claims (via AuthContext) so it's available - * immediately without an extra API call. When the user saves a new currency in settings, - * we call `login()` with the refreshed token — the context automatically picks up the change. - */ - -import { createContext, useContext, useMemo } from 'react'; -import { useAuth } from '../../App'; - -// ISO 4217 currency metadata used for the settings select and price formatting. -export interface CurrencyMeta { - code: string; - label: string; - symbol: string; - locale: string; -} - -export const SUPPORTED_CURRENCIES: CurrencyMeta[] = [ - { code: 'EUR', label: 'Euro', symbol: '€', locale: 'de-DE' }, - { code: 'USD', label: 'US Dollar', symbol: '$', locale: 'en-US' }, - { code: 'GBP', label: 'British Pound', symbol: '£', locale: 'en-GB' }, - { code: 'JPY', label: 'Japanese Yen', symbol: '¥', locale: 'ja-JP' }, - { code: 'CAD', label: 'Canadian Dollar', symbol: 'CA$',locale: 'en-CA' }, - { code: 'AUD', label: 'Australian Dollar', symbol: 'A$', locale: 'en-AU' }, - { code: 'CHF', label: 'Swiss Franc', symbol: 'Fr', locale: 'de-CH' }, - { code: 'CNY', label: 'Chinese Yuan', symbol: '¥', locale: 'zh-CN' }, - { code: 'SEK', label: 'Swedish Krona', symbol: 'kr', locale: 'sv-SE' }, - { code: 'NOK', label: 'Norwegian Krone', symbol: 'kr', locale: 'nb-NO' }, - { code: 'DKK', label: 'Danish Krone', symbol: 'kr', locale: 'da-DK' }, - { code: 'NZD', label: 'New Zealand Dollar', symbol: 'NZ$',locale: 'en-NZ' }, - { code: 'SGD', label: 'Singapore Dollar', symbol: 'S$', locale: 'en-SG' }, - { code: 'HKD', label: 'Hong Kong Dollar', symbol: 'HK$',locale: 'zh-HK' }, - { code: 'KRW', label: 'South Korean Won', symbol: '₩', locale: 'ko-KR' }, - { code: 'BRL', label: 'Brazilian Real', symbol: 'R$', locale: 'pt-BR' }, - { code: 'INR', label: 'Indian Rupee', symbol: '₹', locale: 'en-IN' }, - { code: 'MXN', label: 'Mexican Peso', symbol: 'MX$',locale: 'es-MX' }, - { code: 'PLN', label: 'Polish Złoty', symbol: 'zł', locale: 'pl-PL' }, - { code: 'CZK', label: 'Czech Koruna', symbol: 'Kč', locale: 'cs-CZ' }, - { code: 'HUF', label: 'Hungarian Forint', symbol: 'Ft', locale: 'hu-HU' }, - { code: 'RON', label: 'Romanian Leu', symbol: 'lei',locale: 'ro-RO' }, - { code: 'BGN', label: 'Bulgarian Lev', symbol: 'лв', locale: 'bg-BG' }, - { code: 'TRY', label: 'Turkish Lira', symbol: '₺', locale: 'tr-TR' }, - { code: 'ZAR', label: 'South African Rand', symbol: 'R', locale: 'en-ZA' }, - { code: 'ILS', label: 'Israeli Shekel', symbol: '₪', locale: 'he-IL' }, - { code: 'AED', label: 'UAE Dirham', symbol: 'د.إ',locale: 'ar-AE' }, - { code: 'SAR', label: 'Saudi Riyal', symbol: '﷼', locale: 'ar-SA' }, - { code: 'THB', label: 'Thai Baht', symbol: '฿', locale: 'th-TH' }, - { code: 'MYR', label: 'Malaysian Ringgit', symbol: 'RM', locale: 'ms-MY' }, - { code: 'IDR', label: 'Indonesian Rupiah', symbol: 'Rp', locale: 'id-ID' }, - { code: 'PHP', label: 'Philippine Peso', symbol: '₱', locale: 'fil-PH'}, - { code: 'VND', label: 'Vietnamese Dong', symbol: '₫', locale: 'vi-VN' }, - { code: 'NGN', label: 'Nigerian Naira', symbol: '₦', locale: 'en-NG' }, - { code: 'EGP', label: 'Egyptian Pound', symbol: '£', locale: 'ar-EG' }, - { code: 'UAH', label: 'Ukrainian Hryvnia', symbol: '₴', locale: 'uk-UA' }, -]; - -/** - * Approximate EUR → other currency exchange rates. - * These are static approximations for display purposes only — actual charges are - * always processed in EUR. A production app would fetch live rates from an API. - */ -const EUR_RATES: Record = { - EUR: 1, USD: 1.08, GBP: 0.86, JPY: 163, CAD: 1.47, - AUD: 1.65, CHF: 0.96, CNY: 7.82, SEK: 11.5, NOK: 11.4, - DKK: 7.46, NZD: 1.79, SGD: 1.45, HKD: 8.44, KRW: 1455, - BRL: 5.4, INR: 89.8, MXN: 18.5, PLN: 4.3, CZK: 25.3, - HUF: 390, RON: 4.97, BGN: 1.96, TRY: 34.6, ZAR: 20.2, - ILS: 4.01, AED: 3.97, SAR: 4.05, THB: 37.7, MYR: 5.05, - IDR: 17200, PHP: 62.1, VND: 26900, NGN: 1780, EGP: 52.7, - UAH: 44.7, -}; - -export interface CurrencyContextValue { - currency: string; - currencyMeta: CurrencyMeta; - /** Format EUR cents into the user's preferred display currency */ - formatPrice: (eurCents: number) => string; -} - -export const CurrencyContext = createContext(null); - -/** Hook to access the currency context. Falls back to EUR if used outside the provider. */ -export const useCurrency = (): CurrencyContextValue => { - const ctx = useContext(CurrencyContext); - if (ctx) return ctx; - // Fallback so hooks in non-provider-wrapped code don't crash during init. - return { - currency: 'EUR', - currencyMeta: SUPPORTED_CURRENCIES[0], - formatPrice: (cents) => - new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(cents / 100), - }; -}; - -/** Drop this provider at the root of your app (inside AuthContext) */ -export const CurrencyProvider = ({ children }: { children: React.ReactNode }) => { - const { user } = useAuth(); - - const value = useMemo(() => { - const code = (user as any)?.preferred_currency || 'EUR'; - const meta = SUPPORTED_CURRENCIES.find(c => c.code === code) ?? SUPPORTED_CURRENCIES[0]; - const rate = EUR_RATES[code] ?? 1; - - const formatPrice = (eurCents: number): string => { - if (eurCents === 0) return 'Free'; - const amount = (eurCents / 100) * rate; - try { - return new Intl.NumberFormat(meta.locale, { - style: 'currency', - currency: meta.code, - maximumFractionDigits: meta.code === 'JPY' || meta.code === 'KRW' || meta.code === 'IDR' || meta.code === 'VND' ? 0 : 2, - }).format(amount); - } catch { - return `${meta.symbol}${amount.toFixed(2)}`; - } - }; - - return { currency: code, currencyMeta: meta, formatPrice }; - }, [user]); - - return {children}; -}; diff --git a/src/pages/AuthorProfilePage.tsx b/src/pages/AuthorProfilePage.tsx index e2ee06e..5bd0781 100644 --- a/src/pages/AuthorProfilePage.tsx +++ b/src/pages/AuthorProfilePage.tsx @@ -15,15 +15,16 @@ const AuthorProfilePage = () => { useEffect(() => { setLoading(true); setError(null); - - // Fetch author info and plugins in parallel — saves one full round-trip. - Promise.all([ - api.get(`/user/public/${username}`), - api.get('/plugins', { params: { dev_name: username, sort: 'newest' } }), - ]) - .then(([authorRes, pluginsRes]) => { - setAuthor(authorRes.data); - setPlugins(Array.isArray(pluginsRes.data) ? pluginsRes.data : []); + + // Fetch author public info + api.get(`/user/public/${username}`) + .then(res => { + setAuthor(res.data); + // Fetch author plugins + return api.get('/plugins', { params: { dev_name: username, sort: 'newest' } }); + }) + .then(res => { + setPlugins(Array.isArray(res.data) ? res.data : []); }) .catch(err => { console.error('Fetch error:', err); diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 067a207..bf575dd 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -6,13 +6,10 @@ import SEO from '../components/SEO'; import PluginCard from '../components/PluginCard'; // Global cache to persist data across component unmounts (navigation) -const PAGE_SIZE = 24; - let homeCache: { popular: any[]; newest: any[]; all: any[]; - allHasMore: boolean; } | null = null; const Home = () => { @@ -20,16 +17,13 @@ const Home = () => { const [popular, setPopular] = useState(homeCache?.popular || []); const [newest, setNewest] = useState(homeCache?.newest || []); const [allPlugins, setAllPlugins] = useState(homeCache?.all || []); - const [allHasMore, setAllHasMore] = useState(homeCache?.allHasMore ?? true); - const [allOffset, setAllOffset] = useState(homeCache?.all.length || 0); - const [loadingMore, setLoadingMore] = useState(false); const [loading, setLoading] = useState(false); const [heroIndex, setHeroIndex] = useState(0); const [heroSubImageIndex, setHeroSubImageIndex] = useState(0); const popularRef = useRef(null); const newestRef = useRef(null); - const fetched = useRef({ popular: !!homeCache?.popular.length, newest: !!homeCache?.newest.length, all: !!homeCache?.all.length }); + const fetched = useRef({ popular: !!homeCache?.popular, newest: !!homeCache?.newest, all: !!homeCache?.all }); const saveSection = (key: 'popular' | 'newest' | 'all', data: any[]) => { if (key === 'popular') setPopular(data); @@ -52,23 +46,13 @@ const Home = () => { setLoading(true); try { const [popRes, newRes, allRes] = await Promise.all([ - api.get('/plugins', { params: { sort: 'downloads', limit: 12 } }), - api.get('/plugins', { params: { sort: 'newest', limit: 12 } }), - api.get('/plugins', { params: { limit: PAGE_SIZE, offset: 0 } }) + api.get('/plugins', { params: { sort: 'downloads' } }), + api.get('/plugins', { params: { sort: 'newest' } }), + api.get('/plugins') ]); - const popData = Array.isArray(popRes.data) ? popRes.data : []; - const newData = Array.isArray(newRes.data) ? newRes.data : []; - const allData = Array.isArray(allRes.data) ? allRes.data : []; - const hasMore = allData.length >= PAGE_SIZE; - - setPopular(popData); - setNewest(newData); - setAllPlugins(allData); - setAllHasMore(hasMore); - setAllOffset(allData.length); - - homeCache = { popular: popData, newest: newData, all: allData, allHasMore: hasMore }; - fetched.current = { popular: true, newest: true, all: true }; + saveSection('popular', Array.isArray(popRes.data) ? popRes.data : []); + saveSection('newest', Array.isArray(newRes.data) ? newRes.data : []); + saveSection('all', Array.isArray(allRes.data) ? allRes.data : []); } catch (err) { console.error('Fetch error for home sections:', err); } finally { @@ -79,25 +63,6 @@ const Home = () => { loadSections(); }, []); - const loadMoreAll = async () => { - if (loadingMore || !allHasMore) return; - setLoadingMore(true); - try { - const res = await api.get('/plugins', { params: { limit: PAGE_SIZE, offset: allOffset } }); - const newData = Array.isArray(res.data) ? res.data : []; - const merged = [...allPlugins, ...newData]; - const hasMore = newData.length >= PAGE_SIZE; - setAllPlugins(merged); - setAllHasMore(hasMore); - setAllOffset(merged.length); - if (homeCache) homeCache = { ...homeCache, all: merged, allHasMore: hasMore }; - } catch (err) { - console.error('Load more error:', err); - } finally { - setLoadingMore(false); - } - }; - // Featured plugins for top Steam-like carousel const featuredPlugins = useMemo(() => popular.slice(0, 5), [popular]); @@ -274,7 +239,7 @@ const Home = () => {
-

All Plugins ({allPlugins.length}{allHasMore ? '+' : ''})

+

All Plugins ({allPlugins.length})

{allPlugins.length > 0 ? ( allPlugins.map((plugin) => ( @@ -284,18 +249,6 @@ const Home = () => {
{loading ? 'Loading all plugins...' : 'No plugins available'}
)}
- {allHasMore && ( -
- -
- )}
{(fetched.current?.popular && fetched.current?.newest && popular.length === 0 && newest.length === 0 && allPlugins.length === 0) && ( diff --git a/src/pages/PluginDetail.tsx b/src/pages/PluginDetail.tsx index b16454c..3d2ebd4 100644 --- a/src/pages/PluginDetail.tsx +++ b/src/pages/PluginDetail.tsx @@ -187,7 +187,7 @@ const PluginDetail = () => { setCouponMessage(null); }; - // Ownership — resolved from plugin.owned (injected by server) so no extra HTTP call needed + // Ownership const [ownsPlugin, setOwnsPlugin] = useState(false); const [ownershipChecked, setOwnershipChecked] = useState(false); @@ -201,7 +201,6 @@ const PluginDetail = () => { const [reportSubmitting, setReportSubmitting] = useState(false); const [reportDone, setReportDone] = useState(false); const [changelogOpen, setChangelogOpen] = useState(false); - const [moreByDev, setMoreByDev] = useState([]); // Review Report State const [reviewReportOpen, setReviewReportOpen] = useState(null); @@ -234,41 +233,19 @@ const PluginDetail = () => { } else if (res.data.screenshots?.length > 0) { setMainScreenshot(res.data.screenshots[0].path); } - // Resolve ownership from the plugin response — server includes it when a JWT is sent. - if (res.data.owned !== undefined && res.data.owned !== null) { - setOwnsPlugin(res.data.owned); - setOwnershipChecked(true); - } - // Save to recently viewed - try { - const key = 'recentlyViewed'; - const existing: any[] = JSON.parse(localStorage.getItem(key) || '[]'); - const filtered = existing.filter((p: any) => String(p.id) !== String(res.data.id)); - const entry = { id: res.data.id, name: res.data.name, preview_path: res.data.preview_path, dev_name: res.data.dev_name }; - localStorage.setItem(key, JSON.stringify([entry, ...filtered].slice(0, 10))); - } catch { /* ignore */ } }); }; useEffect(() => { fetchPlugin(); }, [id]); - // ── Fetch more plugins by same developer ───────────────────────────────── + // ── Check ownership ────────────────────────────────────────────────────── useEffect(() => { - if (!plugin?.dev_name || !plugin?.id) return; - api.get('/plugins', { params: { dev_name: plugin.dev_name, limit: 4 } }) - .then(res => { - const data = Array.isArray(res.data) ? res.data : []; - setMoreByDev(data.filter((p: any) => String(p.id) !== String(plugin.id)).slice(0, 3)); - }) - .catch(() => {}); - }, [plugin?.dev_name, plugin?.id]); - - // ── Ownership is now provided by the plugin detail response. - // Fallback: if the server didn't include it (unauthenticated request that later - // becomes authenticated via token refresh), mark it checked immediately. - useEffect(() => { - if (!user) { setOwnershipChecked(true); } - }, [user]); + if (!user || !id) { setOwnershipChecked(true); return; } + api.get(`/plugins/${id}/owned`) + .then(res => setOwnsPlugin(res.data.owned === true)) + .catch(() => setOwnsPlugin(false)) + .finally(() => setOwnershipChecked(true)); + }, [user, id]); // ── Translations ───────────────────────────────────────────────────────── useEffect(() => { @@ -1127,32 +1104,6 @@ const PluginDetail = () => { )}
- - {/* MORE BY THIS DEVELOPER */} - {moreByDev.length > 0 && ( -
-
- More by {plugin.dev_name} -
-
- {moreByDev.map((p: any) => ( - (e.currentTarget.style.background = 'rgba(255,255,255,0.07)')} - onMouseLeave={e => (e.currentTarget.style.background = 'rgba(255,255,255,0.03)')} - > -
-
-
{p.name}
-
{p.type === 'free' ? 'Free' : `€${((p.price_cents || 0) / 100).toFixed(2)}`}
-
- - ))} -
-
- )}
diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index b7be79d..fec844a 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -4,7 +4,6 @@ import { QRCodeSVG } from 'qrcode.react'; import api from '../api'; import { useAuth } from '../App'; import { getCodeList } from 'country-list'; -import { SUPPORTED_CURRENCIES, useCurrency } from '../context/CurrencyContext'; import DeveloperOnboardingModal from '../components/DeveloperOnboardingModal'; import { User, Mail, Globe, Lock, Shield, CreditCard, @@ -36,7 +35,6 @@ const NAV: { key: Tab; label: string; icon: React.FC<{ size?: number }> ; danger const ProfilePage = () => { const { user, login, logout, refreshUser } = useAuth(); - const { currency: currentCurrency } = useCurrency(); const navigate = useNavigate(); const location = useLocation(); @@ -50,7 +48,7 @@ const ProfilePage = () => { }, [location]); const [formData, setFormData] = useState({ - username: '', email: '', country: '', currency: '', + username: '', email: '', country: '', password: '', currentPassword: '', disable2faPassword: '', newPassword: '', confirmPassword: '', }); @@ -81,9 +79,7 @@ const ProfilePage = () => { marketingEmail: false, }); - // Note: We intentionally do NOT call refreshUser() on mount here — the JWT payload already - // contains all the data ProfilePage needs. Individual save actions call login() with the - // updated token when the server returns a new one. + useEffect(() => { refreshUser?.(); }, []); useEffect(() => { if (user) { @@ -92,7 +88,6 @@ const ProfilePage = () => { username: user.username || '', email: user.email || '', country: user.country || '', - currency: user.preferred_currency || currentCurrency || 'EUR', })); setIs2faEnabled(user.totp_enabled || false); } @@ -106,8 +101,7 @@ const ProfilePage = () => { }); useEffect(() => { - // Skip if we already loaded the dev profile or the user isn't a developer. - if (activeTab === 'developer' && user?.is_developer && devProfile === null) { + if (activeTab === 'developer' && user?.is_developer) { api.get('/user/developer/profile') .then(res => { if (res.data.profile) { @@ -128,7 +122,7 @@ const ProfilePage = () => { }) .catch(err => console.error('Failed to load dev profile', err)); } - }, [activeTab, user?.is_developer, devProfile]); + }, [activeTab, user?.is_developer]); const handleUpdateDevProfile = async (e: React.FormEvent) => { e.preventDefault(); @@ -144,15 +138,14 @@ const ProfilePage = () => { }; useEffect(() => { - // Skip if library is already loaded or currently being fetched. - if (activeTab !== 'library' || library.length > 0 || libraryLoading) return; + if (activeTab !== 'library') return; setLibraryLoading(true); setLibraryError(null); api.get('/user/library') .then(res => setLibrary(Array.isArray(res.data) ? res.data : [])) .catch(() => setLibraryError('Failed to load your library. Please try again.')) .finally(() => setLibraryLoading(false)); - }, [activeTab, library.length, libraryLoading]); + }, [activeTab]); const showToast = (text: string, type: 'success' | 'error' = 'success') => { setToast({ text, type }); @@ -441,34 +434,6 @@ const ProfilePage = () => { - - -
{ - if (!formData.currency) { - e.preventDefault(); - showToast('Please select a currency.', 'error'); - return; - } - handleUpdate(e, '/user/change-currency', { newCurrency: formData.currency }, 'currency'); - }}> - - - - - -
)} diff --git a/src/pages/SearchResultsPage.tsx b/src/pages/SearchResultsPage.tsx index d4c9f62..d0f9f32 100644 --- a/src/pages/SearchResultsPage.tsx +++ b/src/pages/SearchResultsPage.tsx @@ -24,38 +24,18 @@ const SearchResultsPage = () => { const [minPrice, setMinPrice] = useState(initialMinPrice); const [maxPrice, setMaxPrice] = useState(initialMaxPrice); - // Debounce filter changes — wait 350ms after user stops typing before firing a request. - const [debouncedQuery, setDebouncedQuery] = useState(searchQuery); - const [debouncedSort, setDebouncedSort] = useState(sort); - const [debouncedCategory, setDebouncedCategory] = useState(category); - const [debouncedType, setDebouncedType] = useState(type); - const [debouncedMinPrice, setDebouncedMinPrice] = useState(minPrice); - const [debouncedMaxPrice, setDebouncedMaxPrice] = useState(maxPrice); - - useEffect(() => { - const timer = setTimeout(() => { - setDebouncedQuery(searchQuery); - setDebouncedSort(sort); - setDebouncedCategory(category); - setDebouncedType(type); - setDebouncedMinPrice(minPrice); - setDebouncedMaxPrice(maxPrice); - }, 350); - return () => clearTimeout(timer); - }, [searchQuery, sort, category, type, minPrice, maxPrice]); - useEffect(() => { const fetchPlugins = async () => { setLoading(true); setError(null); try { const params: any = { - q: debouncedQuery || undefined, - sort: debouncedSort || undefined, - category: debouncedCategory || undefined, - type: debouncedType || undefined, - min_price: debouncedMinPrice ? parseInt(debouncedMinPrice) * 100 : undefined, - max_price: debouncedMaxPrice ? parseInt(debouncedMaxPrice) * 100 : undefined, + q: searchQuery || undefined, + sort: sort || undefined, + category: category || undefined, + type: type || undefined, + min_price: minPrice ? parseInt(minPrice) * 100 : undefined, + max_price: maxPrice ? parseInt(maxPrice) * 100 : undefined, }; const response = await api.get('/plugins', { params }); setPlugins(Array.isArray(response.data) ? response.data : []); @@ -72,15 +52,15 @@ const SearchResultsPage = () => { // Update URL params const newParams: any = {}; - if (debouncedQuery) newParams.q = debouncedQuery; - if (debouncedSort) newParams.sort = debouncedSort; - if (debouncedCategory) newParams.category = debouncedCategory; - if (debouncedType) newParams.type = debouncedType; - if (debouncedMinPrice) newParams.min_price = debouncedMinPrice; - if (debouncedMaxPrice) newParams.max_price = debouncedMaxPrice; + if (searchQuery) newParams.q = searchQuery; + if (sort) newParams.sort = sort; + if (category) newParams.category = category; + if (type) newParams.type = type; + if (minPrice) newParams.min_price = minPrice; + if (maxPrice) newParams.max_price = maxPrice; setSearchParams(newParams, { replace: true }); - }, [debouncedQuery, debouncedSort, debouncedCategory, debouncedType, debouncedMinPrice, debouncedMaxPrice, setSearchParams]); + }, [searchQuery, sort, category, type, minPrice, maxPrice, setSearchParams]); return (
@@ -120,7 +100,7 @@ const SearchResultsPage = () => {
- +
{