Skip to content

Commit cb645e3

Browse files
committed
feat: implement infinite scroll for plugins list
- Added pagination support when loading plugins from API - Implemented scroll event handler to load more plugins when reaching bottom - Set limit of 50 plugins per page - Works in sidebar as well as on plugin page
1 parent 78217ef commit cb645e3

File tree

2 files changed

+94
-9
lines changed

2 files changed

+94
-9
lines changed

src/pages/plugins/plugins.js

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export default function PluginsInclude(updates) {
4444
let $currList = $list.installed;
4545
let currSection = "installed";
4646
let hideSearchBar = () => {};
47+
let currentPage = 1;
48+
let isLoading = false;
49+
let hasMore = true;
50+
const LIMIT = 50;
4751

4852
Contextmenu({
4953
toggler: $add,
@@ -142,6 +146,18 @@ export default function PluginsInclude(updates) {
142146

143147
$page.onclick = handleClick;
144148

149+
$list.all.addEventListener('scroll', (e) => {
150+
if (isLoading || !hasMore) return;
151+
152+
const { scrollTop, scrollHeight, clientHeight } = $currList;
153+
if (scrollTop + clientHeight >= scrollHeight - 100) {
154+
if (currSection === "all") {
155+
currentPage++;
156+
getAllPlugins();
157+
}
158+
}
159+
})
160+
145161
app.append($page);
146162
helpers.showAd();
147163

@@ -199,7 +215,6 @@ export default function PluginsInclude(updates) {
199215
section = currSection;
200216
}
201217

202-
// Hide search bar when changing tabs
203218
if (document.getElementById("search-bar")) {
204219
hideSearchBar();
205220
}
@@ -210,6 +225,8 @@ export default function PluginsInclude(updates) {
210225
$section.scrollTop = $section._scroll || 0;
211226
$currList = $section;
212227
currSection = section;
228+
currentPage = 1;
229+
hasMore = true;
213230
$page.get(".options .active").classList.remove("active");
214231
$page.get(`#${section}_plugins`).classList.add("active");
215232
}
@@ -239,7 +256,7 @@ export default function PluginsInclude(updates) {
239256
$list.all.setAttribute("empty-msg", strings["error"]);
240257
window.log("error", "Failed to search remotely:");
241258
window.log("error", error);
242-
return []; // Return an empty array on error
259+
return [];
243260
}
244261
}
245262

@@ -263,13 +280,26 @@ export default function PluginsInclude(updates) {
263280
}
264281

265282
async function getAllPlugins() {
283+
if (isLoading || !hasMore) return;
284+
266285
try {
267-
plugins.all = [];
286+
isLoading = true;
287+
if (currentPage === 1) {
288+
plugins.all = [];
289+
$list.all.replaceChildren();
290+
}
291+
268292
$list.all.setAttribute("empty-msg", strings["loading..."]);
293+
269294
const installed = await fsOperation(PLUGIN_DIR).lsDir();
270-
plugins.all = await fsOperation(constants.API_BASE, "plugins").readFile(
271-
"json",
272-
);
295+
const response = await fetch(`${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`);
296+
const newPlugins = await response.json();
297+
298+
if (newPlugins.length < LIMIT) {
299+
hasMore = false;
300+
}
301+
302+
plugins.all = [...plugins.all, ...newPlugins];
273303

274304
installed.forEach(({ url }) => {
275305
const plugin = plugins.all.find(({ id }) => id === Url.basename(url));
@@ -286,6 +316,8 @@ export default function PluginsInclude(updates) {
286316
$list.all.setAttribute("empty-msg", strings["no plugins found"]);
287317
} catch (error) {
288318
window.log("error", error);
319+
} finally {
320+
isLoading = false;
289321
}
290322
}
291323

src/sidebarApps/extensions/index.js

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ let container = null;
2020
/** @type {HTMLElement} */
2121
let $searchResult = null;
2222

23+
const LIMIT = 50;
24+
let currentPage = 1;
25+
let hasMore = true;
26+
let isLoading = false;
27+
2328
const $header = (
2429
<div className="header">
2530
<span className="title">
@@ -77,6 +82,7 @@ function initApp(el) {
7782
if (!$explore) {
7883
$explore = collapsableList(strings["explore"]);
7984
$explore.ontoggle = loadExplore;
85+
$explore.$ul.onscroll = handleScroll;
8086
container.append($explore);
8187
}
8288

@@ -90,6 +96,44 @@ function initApp(el) {
9096
Sidebar.on("show", onSelected);
9197
}
9298

99+
async function handleScroll(e) {
100+
if (isLoading || !hasMore) return;
101+
102+
const { scrollTop, scrollHeight, clientHeight } = e.target;
103+
104+
if (scrollTop + clientHeight >= scrollHeight - 50) {
105+
await loadMorePlugins();
106+
}
107+
}
108+
109+
async function loadMorePlugins() {
110+
try {
111+
isLoading = true;
112+
startLoading($explore);
113+
114+
const response = await fetch(
115+
`${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`,
116+
);
117+
const newPlugins = await response.json();
118+
119+
if (newPlugins.length < LIMIT) {
120+
hasMore = false;
121+
}
122+
123+
installedPlugins = await listInstalledPlugins();
124+
const pluginElements = newPlugins.map(ListItem);
125+
$explore.$ul.append(...pluginElements);
126+
127+
currentPage++;
128+
updateHeight($explore);
129+
} catch (error) {
130+
window.log("error", error);
131+
} finally {
132+
isLoading = false;
133+
stopLoading($explore);
134+
}
135+
}
136+
93137
async function searchPlugin() {
94138
clearTimeout(searchTimeout);
95139
searchTimeout = setTimeout(async () => {
@@ -195,12 +239,21 @@ async function loadExplore() {
195239

196240
try {
197241
startLoading($explore);
198-
const plugins = await fsOperation(
199-
Url.join(constants.API_BASE, "plugins?explore=random"),
200-
).readFile("json");
242+
currentPage = 1;
243+
hasMore = true;
244+
245+
const response = await fetch(
246+
`${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`,
247+
);
248+
const plugins = await response.json();
249+
250+
if (plugins.length < LIMIT) {
251+
hasMore = false;
252+
}
201253

202254
installedPlugins = await listInstalledPlugins();
203255
$explore.$ul.content = plugins.map(ListItem);
256+
currentPage++;
204257
updateHeight($explore);
205258
} catch (error) {
206259
$explore.$ul.content = <span className="error">{strings["error"]}</span>;

0 commit comments

Comments
 (0)