Skip to content

refactor: plugins page #1428

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 4 additions & 27 deletions src/pages/plugins/item.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default function Item({
installed,
enabled,
onToggleEnabled,
updates,
}) {
const authorName = (() => {
const displayName =
Expand Down Expand Up @@ -98,12 +99,10 @@ export default function Item({
{price !== null && price !== undefined && price !== 0 ? (
<span className="plugin-price">₹{price}</span>
) : null}
{/* Enable/Disable Toggle */}
{installed && (
{installed && !updates ? (
<span
className="plugin-toggle-switch"
data-enabled={enabled}
// style={{ marginLeft: 12, display: 'flex', alignItems: 'center', gap: 4, cursor: 'pointer', zIndex: 100 }}
onclick={e => {
e.stopPropagation();
onToggleEnabled?.(id, enabled);
Expand All @@ -112,33 +111,11 @@ export default function Item({
<span
className="plugin-toggle-track"
data-enabled={enabled}
// style={{
// width: 36,
// height: 20,
// borderRadius: 12,
// background: enabled ? '#4ade80' : '#d1d5db',
// position: 'relative',
// transition: 'background 0.2s',
// display: 'inline-block',
// }}
>
<span
className="plugin-toggle-thumb"
// style={{
// position: 'absolute',
// left: enabled ? 18 : 2,
// top: 2,
// width: 16,
// height: 16,
// borderRadius: '50%',
// background: '#fff',
// boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
// transition: 'left 0.2s',
// }}
/>
<span className="plugin-toggle-thumb" />
</span>
</span>
)}
) : null}
</div>
</div>
</div>
Expand Down
166 changes: 126 additions & 40 deletions src/pages/plugins/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default function PluginsInclude(updates) {
let isLoading = false;
let hasMore = true;
let isSearching = false;
let currentFilter = null;
const LIMIT = 50;

Contextmenu({
Expand Down Expand Up @@ -81,26 +82,32 @@ export default function PluginsInclude(updates) {
downloads: strings.most_downloaded,
};
const filterName = filterNames[item];
currentFilter = item;
currentPage = 1;
hasMore = true;
isLoading = false;
plugins.all = []; // Reset the all plugins array
render("all");
getFilteredPlugins(item).then((plugins) => {
$list.all.replaceChildren();
$list.all.append(
<div className="filter-message">
Filter for <strong>{filterName}</strong>
<span
className="icon clearclose"
data-action="clear-filter"
onclick={() => {
$list.all.replaceChildren();
getAllPlugins();
}}
></span>
</div>,
);
plugins.forEach((plugin) => {
$list.all.append(<Item {...plugin} />);
});
});
$list.all.replaceChildren();
$list.all.append(
<div className="filter-message">
Filtered by <strong>{filterName}</strong>
<span
className="icon clearclose"
data-action="clear-filter"
onclick={() => {
currentFilter = null;
currentPage = 1;
hasMore = true;
isLoading = false;
plugins.all = []; // Reset the all plugins array
$list.all.replaceChildren();
getAllPlugins();
}}
></span>
</div>,
);
getFilteredPlugins(item);
},
});

Expand Down Expand Up @@ -153,8 +160,12 @@ export default function PluginsInclude(updates) {
if (isLoading || !hasMore || isSearching) return;

const { scrollTop, scrollHeight, clientHeight } = e.target;
if (scrollTop + clientHeight >= scrollHeight - 100) {
await getAllPlugins();
if (scrollTop + clientHeight >= scrollHeight - 50) {
if (currentFilter) {
await getFilteredPlugins(currentFilter);
} else {
await getAllPlugins();
}
}
})

Expand Down Expand Up @@ -237,6 +248,7 @@ export default function PluginsInclude(updates) {
currentPage = 1;
hasMore = true;
isLoading = false;
plugins.all = []; // Reset the all plugins array
$list.all.replaceChildren();
getAllPlugins();
}
Expand Down Expand Up @@ -274,21 +286,56 @@ export default function PluginsInclude(updates) {
}

async function getFilteredPlugins(filterName) {
if (isLoading || !hasMore) return;

try {
isLoading = true;
$list.all.setAttribute("empty-msg", strings["loading..."]);

let response;
if (filterName === "top_rated") {
response = await fetch(`${constants.API_BASE}/plugins?explore=random`);
response = await fetch(`${constants.API_BASE}/plugins?explore=random&page=${currentPage}&limit=${LIMIT}`);
} else {
response = await fetch(
`${constants.API_BASE}/plugin?orderBy=${filterName}`,
`${constants.API_BASE}/plugin?orderBy=${filterName}&page=${currentPage}&limit=${LIMIT}`,
);
}
return await response.json();
const fetchedPlugins = await response.json();

if (fetchedPlugins.length < LIMIT) {
hasMore = false;
}

const installed = await fsOperation(PLUGIN_DIR).lsDir();
const disabledMap = settings.value.pluginsDisabled || {};

installed.forEach(({ url }) => {
const plugin = fetchedPlugins.find(({ id }) => id === Url.basename(url));
if (plugin) {
plugin.installed = true;
plugin.enabled = disabledMap[plugin.id] !== true;
plugin.onToggleEnabled = onToggleEnabled;
plugin.localPlugin = getLocalRes(plugin.id, "plugin.json");
}
});

// Add plugins to the all plugins array
plugins.all.push(...fetchedPlugins);

const fragment = document.createDocumentFragment();
fetchedPlugins.forEach((plugin) => {
fragment.append(<Item {...plugin} updates={updates} />);
});
$list.all.append(fragment);

currentPage++;
$list.all.setAttribute("empty-msg", strings["no plugins found"]);
} catch (error) {
$list.all.setAttribute("empty-msg", strings["error"]);
window.log("error", "Failed to filter plugins:");
window.log("error", error);
return [];
} finally {
isLoading = false;
}
}

Expand All @@ -308,17 +355,26 @@ export default function PluginsInclude(updates) {
}

const installed = await fsOperation(PLUGIN_DIR).lsDir();
const disabledMap = settings.value.pluginsDisabled || {};

installed.forEach(({ url }) => {
const plugin = newPlugins.find(({ id }) => id === Url.basename(url));
if (plugin) {
plugin.installed = true;
plugin.enabled = disabledMap[plugin.id] !== true;
plugin.onToggleEnabled = onToggleEnabled;
plugin.localPlugin = getLocalRes(plugin.id, "plugin.json");
}
});

// Add plugins to the all plugins array
plugins.all.push(...newPlugins);

const fragment = document.createDocumentFragment();
newPlugins.forEach((plugin) => {
$list.all.append(<Item {...plugin} />);
fragment.append(<Item {...plugin} updates={updates} />);
});
$list.all.append(fragment);

currentPage++;
$list.all.setAttribute("empty-msg", strings["no plugins found"]);
Expand Down Expand Up @@ -347,7 +403,7 @@ export default function PluginsInclude(updates) {
plugin.onToggleEnabled = onToggleEnabled;
plugins.installed.push(plugin);
if ($list.installed.get(`[data-id=\"${id}\"]`)) return;
$list.installed.append(<Item {...plugin} />);
$list.installed.append(<Item {...plugin} updates={updates} />);
}),
);
$list.installed.setAttribute("empty-msg", strings["no plugins found"]);
Expand All @@ -356,14 +412,22 @@ export default function PluginsInclude(updates) {
async function getOwned() {
$list.owned.setAttribute("empty-msg", strings["loading..."]);
const purchases = await helpers.promisify(iap.getPurchases);
const disabledMap = settings.value.pluginsDisabled || {};

purchases.forEach(async ({ productIds }) => {
const [sku] = productIds;
const url = Url.join(constants.API_BASE, "plugin/owned", sku);
const plugin = await fsOperation(url).readFile("json");
const isInstalled = plugins.installed.find(({ id }) => id === plugin.id);
plugin.installed = !!isInstalled;

if (plugin.installed) {
plugin.enabled = disabledMap[plugin.id] !== true;
plugin.onToggleEnabled = onToggleEnabled;
}

plugins.owned.push(plugin);
$list.owned.append(<Item {...plugin} />);
$list.owned.append(<Item {...plugin} updates={updates} />);
});
$list.owned.setAttribute("empty-msg", strings["no plugins found"]);
}
Expand Down Expand Up @@ -392,7 +456,7 @@ export default function PluginsInclude(updates) {

const existingItem = $list.installed.get(`[data-id="${plugin.id}"]`);
if (!existingItem) {
$list.installed.append(<Item {...plugin} />);
$list.installed.append(<Item {...plugin} updates={updates} />);
}

}
Expand Down Expand Up @@ -456,17 +520,39 @@ export default function PluginsInclude(updates) {
window.toast(strings["plugin_enabled"] || "Plugin enabled");
}

// Update the plugin object's state
const plugin = plugins.installed.find(p => p.id === id);
if (plugin) {
plugin.enabled = !enabled;

// Re-render the specific item
const $existingItem = $list.installed.get(`[data-id="${id}"]`);
if ($existingItem) {
const $newItem = <Item {...plugin} />;
$existingItem.replaceWith($newItem);
}
// Update the plugin object's state in all plugin arrays
const installedPlugin = plugins.installed.find(p => p.id === id);
if (installedPlugin) {
installedPlugin.enabled = !enabled;
}

const allPlugin = plugins.all.find(p => p.id === id);
if (allPlugin) {
allPlugin.enabled = !enabled;
}

const ownedPlugin = plugins.owned.find(p => p.id === id);
if (ownedPlugin) {
ownedPlugin.enabled = !enabled;
}

// Re-render the specific item in all tabs
const $installedItem = $list.installed.get(`[data-id="${id}"]`);
if ($installedItem && installedPlugin) {
const $newItem = <Item {...installedPlugin} updates={updates} />;
$installedItem.replaceWith($newItem);
}

const $allItem = $list.all.get(`[data-id="${id}"]`);
if ($allItem && allPlugin) {
const $newItem = <Item {...allPlugin} updates={updates} />;
$allItem.replaceWith($newItem);
}

const $ownedItem = $list.owned.get(`[data-id="${id}"]`);
if ($ownedItem && ownedPlugin) {
const $newItem = <Item {...ownedPlugin} updates={updates} />;
$ownedItem.replaceWith($newItem);
}
}
}
Loading