Files
Homarr/src/components/AppShelf/AppShelf.tsx

208 lines
6.7 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
2022-08-02 00:19:39 +02:00
import { Accordion, Grid, Paper, Stack, useMantineColorScheme } from '@mantine/core';
import {
closestCenter,
DndContext,
DragOverlay,
MouseSensor,
TouchSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import { arrayMove, SortableContext } from '@dnd-kit/sortable';
import { useLocalStorage } from '@mantine/hooks';
2022-08-22 09:50:54 +02:00
import { useTranslation } from 'next-i18next';
import { useConfig } from '../../tools/state';
2022-04-25 00:11:32 +02:00
import { SortableAppShelfItem, AppShelfItem } from './AppShelfItem';
import { ModuleMenu, ModuleWrapper } from '../../modules/moduleWrapper';
2022-08-25 18:47:06 +02:00
import { UsenetModule, TorrentsModule } from '../../modules';
2022-08-25 18:10:23 +02:00
import TorrentsComponent from '../../modules/torrents/TorrentsModule';
2022-08-31 18:00:10 +02:00
import UsenetComponent from '../../modules/usenet/UsenetModule';
2022-05-17 01:43:40 +02:00
2022-05-16 15:55:22 +02:00
const AppShelf = (props: any) => {
2022-08-02 00:19:39 +02:00
const { config, setConfig } = useConfig();
// Extract all the categories from the services in config
const categoryList = config.services.reduce((acc, cur) => {
if (cur.category && !acc.includes(cur.category)) {
acc.push(cur.category);
}
return acc;
}, [] as string[]);
const [toggledCategories, setToggledCategories] = useLocalStorage({
key: 'app-shelf-toggled',
2022-08-02 00:19:39 +02:00
// This is a bit of a hack to toggle the categories on the first load, return a string[] of the categories
defaultValue: categoryList,
});
const [activeId, setActiveId] = useState(null);
const { colorScheme } = useMantineColorScheme();
2022-08-22 09:50:54 +02:00
const { t } = useTranslation('layout/app-shelf');
const sensors = useSensors(
useSensor(TouchSensor, {
activationConstraint: {
delay: 500,
tolerance: 5,
},
}),
useSensor(MouseSensor, {
// Require the mouse to move by 10 pixels before activating
activationConstraint: {
delay: 500,
tolerance: 5,
},
})
2022-05-04 07:12:22 +02:00
);
function handleDragStart(event: any) {
const { active } = event;
setActiveId(active.id);
}
function handleDragEnd(event: any) {
const { active, over } = event;
if (active.id !== over.id) {
const newConfig = { ...config };
const activeIndex = newConfig.services.findIndex((e) => e.id === active.id);
const overIndex = newConfig.services.findIndex((e) => e.id === over.id);
newConfig.services = arrayMove(newConfig.services, activeIndex, overIndex);
setConfig(newConfig);
}
setActiveId(null);
}
2022-05-29 10:45:49 +02:00
2022-08-02 00:19:39 +02:00
const getItems = (filter?: string) => {
2022-05-29 10:45:49 +02:00
// If filter is not set, return all the services without a category or a null category
let filtered = config.services;
if (!filter) {
filtered = config.services.filter((e) => !e.category || e.category === null);
}
if (filter) {
filtered = config.services.filter((e) => e.category === filter);
}
2022-05-29 10:45:49 +02:00
return (
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
>
2022-05-29 10:45:49 +02:00
<SortableContext items={config.services}>
<Grid gutter="xl" align="center">
{filtered.map((service) => (
<Grid.Col
key={service.id}
span={6}
xl={config.settings.appCardWidth || 2}
xs={4}
sm={3}
md={3}
>
2022-05-29 10:45:49 +02:00
<SortableAppShelfItem service={service} key={service.id} id={service.id} />
</Grid.Col>
))}
</Grid>
</SortableContext>
<DragOverlay
style={{
// Add a shadow to the drag overlay
boxShadow: '0 0 10px rgba(0, 0, 0, 0.5)',
}}
>
{activeId ? (
<AppShelfItem service={config.services.find((e) => e.id === activeId)} id={activeId} />
) : null}
</DragOverlay>
</DndContext>
);
};
if (categoryList.length > 0) {
const noCategory = config.services.filter(
(e) => e.category === undefined || e.category === null
);
2022-08-31 18:00:10 +02:00
const usenetEnabled = config.modules?.[TorrentsModule.id]?.enabled ?? false;
const torrentEnabled = config.modules?.[UsenetModule.id]?.enabled ?? false;
const downloadEnabled = usenetEnabled || torrentEnabled;
// Create an item with 0: true, 1: true, 2: true... For each category
2022-05-29 10:45:49 +02:00
return (
2022-08-02 00:19:39 +02:00
// TODO: Style accordion so that the bar is transparent to the user settings
<Stack>
<Accordion
2022-08-02 00:19:39 +02:00
variant="separated"
radius="lg"
order={2}
multiple
2022-08-02 00:19:39 +02:00
value={toggledCategories}
onChange={(state) => {
setToggledCategories([...state]);
2022-08-02 00:19:39 +02:00
}}
>
{categoryList.map((category, idx) => (
2022-08-02 00:19:39 +02:00
<Accordion.Item key={category} value={idx.toString()}>
<Accordion.Control>{category}</Accordion.Control>
<Accordion.Panel>{getItems(category)}</Accordion.Panel>
</Accordion.Item>
))}
{/* Return the item for all services without category */}
{noCategory && noCategory.length > 0 ? (
2022-08-02 00:19:39 +02:00
<Accordion.Item key="Other" value="Other">
2022-08-26 11:33:55 +02:00
<Accordion.Control>{t('accordions.others.text')}</Accordion.Control>
2022-08-02 00:19:39 +02:00
<Accordion.Panel>{getItems()}</Accordion.Panel>
</Accordion.Item>
) : null}
{downloadEnabled ? (
2022-08-02 00:19:39 +02:00
<Accordion.Item key="Downloads" value="Your downloads">
2022-08-22 09:50:54 +02:00
<Accordion.Control>{t('accordions.downloads.text')}</Accordion.Control>
2022-08-02 00:19:39 +02:00
<Accordion.Panel>
<Paper
p="lg"
radius="lg"
style={{
background: `rgba(${colorScheme === 'dark' ? '37, 38, 43,' : '255, 255, 255,'} \
${(config.settings.appOpacity || 100) / 100}`,
2022-08-02 00:19:39 +02:00
borderColor: `rgba(${
colorScheme === 'dark' ? '37, 38, 43,' : '233, 236, 239,'
} \
${(config.settings.appOpacity || 100) / 100}`,
2022-08-02 00:19:39 +02:00
}}
>
2022-08-31 18:00:10 +02:00
{torrentEnabled && (
<>
<ModuleMenu module={TorrentsModule} />
<TorrentsComponent />
</>
)}
{usenetEnabled && (
<>
<ModuleMenu module={UsenetModule} />
<UsenetComponent />
</>
)}
2022-08-02 00:19:39 +02:00
</Paper>
</Accordion.Panel>
</Accordion.Item>
) : null}
</Accordion>
</Stack>
2022-05-29 10:45:49 +02:00
);
}
return (
<Stack>
2022-08-02 00:19:39 +02:00
{getItems()}
2022-08-25 18:10:23 +02:00
<ModuleWrapper mt="xl" module={TorrentsModule} />
2022-08-25 18:47:06 +02:00
<ModuleWrapper mt="xl" module={UsenetModule} />
</Stack>
);
};
2022-04-25 00:11:32 +02:00
2022-04-27 03:11:35 +02:00
export default AppShelf;