Skip to content

Commit 56bcb62

Browse files
committed
chore: change home sorting
1 parent 1de42e6 commit 56bcb62

4 files changed

Lines changed: 110 additions & 22 deletions

File tree

src/App.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,14 @@ h3 {
553553
}
554554

555555
/* --- Plugin Grid --- */
556+
.home-section {
557+
margin-bottom: 4rem;
558+
}
559+
560+
.home-section:last-child {
561+
margin-bottom: 0;
562+
}
563+
556564
.plugin-grid,
557565
.home-plugin-grid {
558566
display: grid;

src/App.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { createContext, useContext, useState, useEffect } from 'react';
2-
import { BrowserRouter as Router, Routes, Route, Link, NavLink, useNavigate, Outlet, Navigate } from 'react-router-dom';
2+
import { BrowserRouter as Router, Routes, Route, Link, NavLink, useNavigate, Outlet, Navigate, useLocation } from 'react-router-dom';
33
import { useTranslation } from 'react-i18next';
44
import Home from './pages/Home';
55
import PluginDetail from './pages/PluginDetail';
@@ -25,6 +25,15 @@ import './App.css';
2525

2626
import api from './api';
2727

28+
// --- Scroll To Top Helper ---
29+
const ScrollToTop = () => {
30+
const { pathname } = useLocation();
31+
useEffect(() => {
32+
window.scrollTo(0, 0);
33+
}, [pathname]);
34+
return null;
35+
};
36+
2837
// --- Auth Context ---
2938
const AuthContext = createContext<any>(null);
3039
export const useAuth = () => useContext(AuthContext);
@@ -112,6 +121,7 @@ const AdminRoute = ({ children }: { children: React.ReactNode }) => {
112121
const App = () => (
113122
<Router>
114123
<AuthProvider>
124+
<ScrollToTop />
115125
<Routes>
116126
{/* All routes share MainLayout (navbar + footer) */}
117127
<Route path="/" element={<MainLayout />}>
@@ -211,7 +221,10 @@ const Navbar = ({ user }: any) => {
211221
<div className={`nav-content ${isMenuOpen ? 'open' : ''}`}>
212222
<div className="nav-links">
213223
<NavLink to="/" onClick={() => setIsMenuOpen(false)}>{t('home')}</NavLink>
214-
{user && <NavLink to="/dashboard" onClick={() => setIsMenuOpen(false)}>{t('dashboard')}</NavLink>}
224+
{user && (user.plugin_count > 0 || user.role === 'admin') && (
225+
<NavLink to="/dashboard" onClick={() => setIsMenuOpen(false)}>{t('dashboard')}</NavLink>
226+
)}
227+
{user && <NavLink to="/dashboard/add-plugin" onClick={() => setIsMenuOpen(false)}>Publish</NavLink>}
215228
{user && user.role === 'admin' && <NavLink to="/admin" onClick={() => setIsMenuOpen(false)}>Admin</NavLink>}
216229
</div>
217230

src/pages/Home.tsx

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,64 @@ import api from '../api';
44
import SEO from '../components/SEO';
55
import PluginCard from '../components/PluginCard';
66

7+
// Global cache to persist data across component unmounts (navigation)
8+
// This allows the page to render immediately when going "back",
9+
// which is required for the browser to restore scroll position.
10+
let homeCache: {
11+
topPaid: any[];
12+
popular: any[];
13+
newest: any[];
14+
} | null = null;
15+
716
const Home = () => {
817
const { t } = useTranslation();
9-
const [plugins, setPlugins] = useState<any[]>([]);
10-
const hasFetched = useRef(false);
18+
const [topPaid, setTopPaid] = useState<any[]>(homeCache?.topPaid || []);
19+
const [popular, setPopular] = useState<any[]>(homeCache?.popular || []);
20+
const [newest, setNewest] = useState<any[]>(homeCache?.newest || []);
21+
const [loading, setLoading] = useState(!homeCache);
22+
const hasFetched = useRef(!!homeCache);
1123

1224
useEffect(() => {
1325
if (hasFetched.current) return;
14-
api.get('/plugins', { params: { q: '', sort: 'newest' } })
15-
.then(res => {
16-
setPlugins(Array.isArray(res.data) ? res.data : []);
26+
27+
const fetchSections = async () => {
28+
try {
29+
const [topPaidRes, popularRes, newestRes] = await Promise.all([
30+
api.get('/plugins', { params: { type: 'paid', sort: 'downloads', limit: 4 } }),
31+
api.get('/plugins', { params: { sort: 'downloads', limit: 8 } }),
32+
api.get('/plugins', { params: { sort: 'newest', limit: 12 } })
33+
]);
34+
35+
const data = {
36+
topPaid: Array.isArray(topPaidRes.data) ? topPaidRes.data : [],
37+
popular: Array.isArray(popularRes.data) ? popularRes.data : [],
38+
newest: Array.isArray(newestRes.data) ? newestRes.data : []
39+
};
40+
41+
setTopPaid(data.topPaid);
42+
setPopular(data.popular);
43+
setNewest(data.newest);
44+
homeCache = data;
1745
hasFetched.current = true;
18-
})
19-
.catch(err => {
46+
} catch (err) {
2047
console.error('Fetch error:', err);
21-
setPlugins([]);
22-
});
48+
} finally {
49+
setLoading(false);
50+
}
51+
};
52+
53+
fetchSections();
2354
}, []);
2455

56+
if (loading) {
57+
return (
58+
<div className="loading-state">
59+
<div className="spinner"></div>
60+
<p>Discovering best plugins...</p>
61+
</div>
62+
);
63+
}
64+
2565
return (
2666
<>
2767
<SEO
@@ -35,17 +75,40 @@ const Home = () => {
3575
</section>
3676

3777
<div className="container" id="browse">
38-
<div className="home-plugin-grid">
39-
{plugins.map((plugin) => (
40-
<PluginCard key={plugin.id} plugin={plugin} />
41-
))}
42-
43-
{plugins.length === 0 && (
44-
<div className="empty-state">
45-
<p>No plugins found. Check back later!</p>
78+
{topPaid.length > 0 && (
79+
<section className="home-section">
80+
<h2 className="section-title"><span>Top</span> Premium</h2>
81+
<div className="home-plugin-grid">
82+
{topPaid.map((plugin) => (
83+
<PluginCard key={`top-${plugin.id}`} plugin={plugin} />
84+
))}
4685
</div>
47-
)}
48-
</div>
86+
</section>
87+
)}
88+
89+
<section className="home-section">
90+
<h2 className="section-title"><span>Most</span> Popular</h2>
91+
<div className="home-plugin-grid">
92+
{popular.map((plugin) => (
93+
<PluginCard key={`pop-${plugin.id}`} plugin={plugin} />
94+
))}
95+
</div>
96+
</section>
97+
98+
<section className="home-section">
99+
<h2 className="section-title"><span>New</span> & Trending</h2>
100+
<div className="home-plugin-grid">
101+
{newest.map((plugin) => (
102+
<PluginCard key={`new-${plugin.id}`} plugin={plugin} />
103+
))}
104+
</div>
105+
</section>
106+
107+
{topPaid.length === 0 && popular.length === 0 && newest.length === 0 && (
108+
<div className="empty-state">
109+
<p>No plugins found. Check back later!</p>
110+
</div>
111+
)}
49112
</div>
50113
</>
51114
);

src/pages/dashboard/plugin/AddPlugin.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ const LangPicker = ({ usedCodes, onAdd, onClose }: { usedCodes: string[]; onAdd:
191191
// ── Main ──────────────────────────────────────────────────────────────────────
192192
const AddPlugin = () => {
193193
const navigate = useNavigate();
194-
const { user } = useAuth();
194+
const { user, refreshUser } = useAuth();
195195
const [step, setStep] = useState(0);
196196
const [submitting, setSubmitting] = useState(false);
197197
const [done, setDone] = useState(false);
@@ -362,6 +362,10 @@ const AddPlugin = () => {
362362
if (wasmFile) fd.append('wasm', wasmFile);
363363
screenshots.forEach(f => fd.append('screenshots', f));
364364
await api.post('/plugins', fd);
365+
366+
// Refresh user to update plugin_count in JWT for Navbar
367+
await refreshUser();
368+
365369
setDone(true);
366370
} catch (err: any) {
367371
console.error(err);

0 commit comments

Comments
 (0)