feat: library display option, navbar count

This commit is contained in:
David Ralph
2026-01-28 15:49:22 +00:00
parent 05bf8edeea
commit a4e575c5f6
10 changed files with 248 additions and 48 deletions

View File

@@ -84,14 +84,14 @@ function ItemCard({ item, toggleFunction, type, onCollection, isCurator, isInsta
{isSideloaded && (
<Tooltip
title={variables.getMessage('modals.main.addons.sideload.title')}
style={{ position: 'absolute', top: '12px', right: '48px', zIndex: 2 }}
style={{ position: 'absolute', top: '12px', right: isAdded ? '48px' : '12px', zIndex: 2 }}
>
<div className="item-sideload-badge">
<MdOutlineUploadFile />
</div>
</Tooltip>
)}
{isInstalled && item.colour && !isSideloaded && (
{isInstalled && item.colour && !isSideloaded && !isAdded && (
<div className="item-installed-badge">
<MdCheckCircle />
</div>
@@ -148,6 +148,7 @@ function Items({
onSortChange,
isAdded = false,
onUninstall,
viewType = 'grid',
}) {
const [selectedCategory, setSelectedCategory] = useState('all');
const [sortType, setSortType] = useState(localStorage.getItem('sortMarketplace') || 'a-z');
@@ -200,7 +201,7 @@ function Items({
/>
</div>
)}
<div className='items'>
<div className={`items ${viewType === 'list' ? 'items-list' : 'items-grid'}`}>
{items
?.filter((item) => filterItems(item, filter, filterOptions ? selectedCategory : 'all'))
.map((item, index) => (

View File

@@ -14,6 +14,7 @@ export const useMarketplaceInstall = () => {
toast(variables.getMessage('toasts.installed'));
variables.stats.postEvent('marketplace-item', `${data.display_name || data.name} installed`);
variables.stats.postEvent('marketplace', 'Install');
window.dispatchEvent(new Event('installedAddonsChanged'));
};
const uninstallItem = (type, name) => {
@@ -21,6 +22,7 @@ export const useMarketplaceInstall = () => {
toast(variables.getMessage('toasts.uninstalled'));
variables.stats.postEvent('marketplace-item', `${name} uninstalled`);
variables.stats.postEvent('marketplace', 'Uninstall');
window.dispatchEvent(new Event('installedAddonsChanged'));
};
const installCollection = async (items) => {
@@ -51,6 +53,7 @@ export const useMarketplaceInstall = () => {
}
toast(variables.getMessage('toasts.installed'));
window.dispatchEvent(new Event('installedAddonsChanged'));
window.location.reload();
} catch (error) {
if (!controllerRef.current.signal.aborted) {

View File

@@ -1,6 +1,6 @@
import variables from 'config/variables';
import { memo, useState, useEffect, useCallback } from 'react';
import { MdUpdate, MdOutlineExtensionOff, MdSendTimeExtension, MdExplore } from 'react-icons/md';
import { MdUpdate, MdOutlineExtensionOff, MdSendTimeExtension, MdExplore, MdViewModule, MdViewList } from 'react-icons/md';
import { toast } from 'react-toastify';
import Modal from 'react-modal';
@@ -17,6 +17,7 @@ const Added = memo(() => {
const [installed, setInstalled] = useState(JSON.parse(localStorage.getItem('installed')));
const [showFailed, setShowFailed] = useState(false);
const [failedReason, setFailedReason] = useState('');
const [viewType, setViewType] = useState(localStorage.getItem('addonsViewType') || 'grid');
const installAddon = useCallback((input) => {
let failedReasonText = '';
@@ -55,6 +56,7 @@ const Added = memo(() => {
toast(variables.getMessage('toasts.installed'));
variables.stats.postEvent('marketplace', 'Sideload');
setInstalled(JSON.parse(localStorage.getItem('installed')));
window.dispatchEvent(new Event('installedAddonsChanged'));
}, []);
const getSideloadButton = useCallback(() => {
@@ -149,12 +151,14 @@ const Added = memo(() => {
localStorage.setItem('installed', JSON.stringify([]));
toast(variables.getMessage('toasts.uninstalled_all'));
setInstalled([]);
window.dispatchEvent(new Event('installedAddonsChanged'));
}, [installed]);
const handleUninstall = useCallback((type, name) => {
uninstall(type, name);
toast(variables.getMessage('toasts.uninstalled'));
setInstalled(JSON.parse(localStorage.getItem('installed')));
window.dispatchEvent(new Event('installedAddonsChanged'));
}, []);
useEffect(() => {
@@ -192,6 +196,11 @@ const Added = memo(() => {
window.dispatchEvent(event);
}, []);
const toggleViewType = useCallback((type) => {
setViewType(type);
localStorage.setItem('addonsViewType', type);
}, []);
if (installed.length === 0) {
return (
<>
@@ -240,16 +249,34 @@ const Added = memo(() => {
/>
</CustomActions>
</Header>
<Dropdown
label={variables.getMessage('modals.main.addons.sort.title')}
name="sortAddons"
onChange={(value) => sortAddons(value)}
items={[
{ value: 'newest', text: variables.getMessage('modals.main.addons.sort.newest') },
{ value: 'a-z', text: variables.getMessage('modals.main.addons.sort.a_z') },
{ value: 'recently-updated', text: 'Recently Updated' },
]}
/>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '15px', marginBottom: '15px' }}>
<Dropdown
label={variables.getMessage('modals.main.addons.sort.title')}
name="sortAddons"
onChange={(value) => sortAddons(value)}
items={[
{ value: 'newest', text: variables.getMessage('modals.main.addons.sort.newest') },
{ value: 'a-z', text: variables.getMessage('modals.main.addons.sort.a_z') },
{ value: 'recently-updated', text: 'Recently Updated' },
]}
/>
<div className="view-toggle-buttons">
<button
className={`view-toggle-btn ${viewType === 'grid' ? 'active' : ''}`}
onClick={() => toggleViewType('grid')}
aria-label="Grid view"
>
<MdViewModule />
</button>
<button
className={`view-toggle-btn ${viewType === 'list' ? 'active' : ''}`}
onClick={() => toggleViewType('list')}
aria-label="List view"
>
<MdViewList />
</button>
</div>
</div>
<Items
items={installed}
isAdded={true}
@@ -257,6 +284,7 @@ const Added = memo(() => {
toggleFunction={(input) => toggle('item', input)}
showCreateYourOwn={false}
onUninstall={handleUninstall}
viewType={viewType}
/>
</>
);

View File

@@ -40,6 +40,7 @@ const tryInstallDefaultPack = async () => {
);
const { data } = await response.json();
install(data.type, data, false, true);
window.dispatchEvent(new Event('installedAddonsChanged'));
return true;
} catch (e) {
console.error('Failed to install default pack:', e);