Skip to content

Commit

Permalink
🚀 v0.7.0 : Theming, Password protection, Autocompletion, Transmission…
Browse files Browse the repository at this point in the history
…, Mobile responsiveness! This is a big upgrade 👀
  • Loading branch information
ajnart committed Jun 13, 2022
2 parents e718fd6 + 5b14375 commit 778988d
Show file tree
Hide file tree
Showing 56 changed files with 1,667 additions and 593 deletions.
7 changes: 0 additions & 7 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,3 @@
### Screenshot _(if applicable)_
> If you've introduced any significant UI changes, please include a screenshot.
### Code Quality Checklist _(Please complete)_
- [ ] All changes are backwards compatible
- [ ] There are no (new) build warnings or errors
- [ ] _(If a new config option is added)_ Attribute is outlined in the schema and documented
- [ ] _(If a new dependency is added)_ Package is essential, and has been checked out for security or performance
- [ ] Bumps version, if new feature added
2 changes: 1 addition & 1 deletion .github/workflows/docker_dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
workflow_dispatch:
inputs:
tags:
requierd: true
required: true
description: 'Tags to deploy to'

env:
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ COPY /.next/standalone ./
COPY /.next/static ./.next/static
EXPOSE 7575
ENV PORT 7575
RUN apk add tzdata
VOLUME /app/data/configs
CMD ["node", "server.js"]
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,4 @@ SOFTWARE.
<i>Thank you for visiting! <b>For more information <a href="https://github.com/ajnart/homarr/wiki">read the wiki!</a></b></i>
<br/>
<br/>
<a href="https://trackgit.com">
<img src="https://us-central1-trackgit-analytics.cloudfunctions.net/token/ping/l3khzc3a3pexzw5w5whl" alt="trackgit-views" />
</a>
</p>
2 changes: 1 addition & 1 deletion data/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const REPO_URL = 'ajnart/homarr';
export const CURRENT_VERSION = 'v0.6.0';
export const CURRENT_VERSION = 'v0.7.0';
23 changes: 12 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "homarr",
"version": "0.6.0",
"version": "0.7.0",
"description": "Homarr - A homepage for your server.",
"repository": {
"type": "git",
Expand All @@ -24,26 +24,27 @@
"ci": "yarn test && yarn lint --fix && yarn typecheck && yarn prettier:write"
},
"dependencies": {
"@ctrl/deluge": "^4.0.0",
"@ctrl/deluge": "^4.1.0",
"@ctrl/qbittorrent": "^4.0.0",
"@ctrl/shared-torrent": "^4.1.0",
"@ctrl/transmission": "^4.1.1",
"@dnd-kit/core": "^6.0.1",
"@dnd-kit/sortable": "^7.0.0",
"@dnd-kit/utilities": "^3.2.0",
"@mantine/core": "^4.2.6",
"@mantine/dates": "^4.2.6",
"@mantine/dropzone": "^4.2.6",
"@mantine/form": "^4.2.6",
"@mantine/hooks": "^4.2.6",
"@mantine/next": "^4.2.6",
"@mantine/notifications": "^4.2.6",
"@mantine/prism": "^4.2.6",
"@mantine/core": "^4.2.8",
"@mantine/dates": "^4.2.8",
"@mantine/dropzone": "^4.2.8",
"@mantine/form": "^4.2.8",
"@mantine/hooks": "^4.2.8",
"@mantine/next": "^4.2.8",
"@mantine/notifications": "^4.2.8",
"@mantine/prism": "^4.2.8",
"@nivo/core": "^0.79.0",
"@nivo/line": "^0.79.1",
"@tabler/icons": "^1.68.0",
"axios": "^0.27.2",
"cookies-next": "^2.0.4",
"dayjs": "^1.11.2",
"dayjs": "^1.11.3",
"framer-motion": "^6.3.1",
"js-file-download": "^0.4.12",
"next": "12.1.6",
Expand Down
54 changes: 31 additions & 23 deletions src/components/AppShelf/AddAppShelfItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import {
Text,
} from '@mantine/core';
import { useForm } from '@mantine/form';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { IconApps as Apps } from '@tabler/icons';
import { v4 as uuidv4 } from 'uuid';
import { useDebouncedValue } from '@mantine/hooks';
import { useConfig } from '../../tools/state';
import { ServiceTypeList } from '../../tools/types';

Expand Down Expand Up @@ -64,24 +65,24 @@ function MatchIcon(name: string, form: any) {
}

function MatchService(name: string, form: any) {
const service = ServiceTypeList.find((s) => s === name);
const service = ServiceTypeList.find((s) => s.toLowerCase() === name.toLowerCase());
if (service) {
form.setFieldValue('type', service);
}
}

function MatchPort(name: string, form: any) {
const portmap = [
{ name: 'qBittorrent', value: '8080' },
{ name: 'Sonarr', value: '8989' },
{ name: 'Radarr', value: '7878' },
{ name: 'Lidarr', value: '8686' },
{ name: 'Readarr', value: '8686' },
{ name: 'Deluge', value: '8112' },
{ name: 'Transmission', value: '9091' },
{ name: 'qbittorrent', value: '8080' },
{ name: 'sonarr', value: '8989' },
{ name: 'radarr', value: '7878' },
{ name: 'lidarr', value: '8686' },
{ name: 'readarr', value: '8686' },
{ name: 'deluge', value: '8112' },
{ name: 'transmission', value: '9091' },
];
// Match name with portmap key
const port = portmap.find((p) => p.name === name);
const port = portmap.find((p) => p.name === name.toLowerCase());
if (port) {
form.setFieldValue('url', `http://localhost:${port.value}`);
}
Expand Down Expand Up @@ -111,6 +112,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
apiKey: props.apiKey ?? (undefined as unknown as string),
username: props.username ?? (undefined as unknown as string),
password: props.password ?? (undefined as unknown as string),
openedUrl: props.openedUrl ?? (undefined as unknown as string),
},
validate: {
apiKey: () => null,
Expand All @@ -134,6 +136,14 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
},
});

const [debounced, cancel] = useDebouncedValue(form.values.name, 250);
useEffect(() => {
if (form.values.name !== debounced || props.name || props.type) return;
MatchIcon(form.values.name, form);
MatchService(form.values.name, form);
MatchPort(form.values.name, form);
}, [debounced]);

// Try to set const hostname to new URL(form.values.url).hostname)
// If it fails, set it to the form.values.url
let hostname = form.values.url;
Expand Down Expand Up @@ -186,28 +196,26 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
required
label="Service name"
placeholder="Plex"
value={form.values.name}
onChange={(event) => {
form.setFieldValue('name', event.currentTarget.value);
MatchIcon(event.currentTarget.value, form);
MatchService(event.currentTarget.value, form);
MatchPort(event.currentTarget.value, form);
}}
error={form.errors.name && 'Invalid icon url'}
{...form.getInputProps('name')}
/>

<TextInput
required
label="Icon url"
placeholder="https://i.gifer.com/ANPC.gif"
label="Icon URL"
placeholder="/favicon.svg"
{...form.getInputProps('icon')}
/>
<TextInput
required
label="Service url"
label="Service URL"
placeholder="http://localhost:7575"
{...form.getInputProps('url')}
/>
<TextInput
label="New tab URL"
placeholder="http://sonarr.example.com"
{...form.getInputProps('openedUrl')}
/>
<Select
label="Service type"
defaultValue="Other"
Expand Down Expand Up @@ -292,12 +300,12 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
/>
</>
)}
{form.values.type === 'Deluge' && (
{(form.values.type === 'Deluge' || form.values.type === 'Transmission') && (
<>
<TextInput
required
label="Password"
placeholder="deluge"
placeholder="password"
value={form.values.password}
onChange={(event) => {
form.setFieldValue('password', event.currentTarget.value);
Expand Down
89 changes: 70 additions & 19 deletions src/components/AppShelf/AppShelf.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,50 @@
import React, { useState } from 'react';
import { Grid, Group, Title } from '@mantine/core';
import { Accordion, createStyles, Grid, Group, Paper, 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';
import { useConfig } from '../../tools/state';

import { SortableAppShelfItem, AppShelfItem } from './AppShelfItem';
import { ModuleWrapper } from '../modules/moduleWrapper';
import { ModuleMenu, ModuleWrapper } from '../modules/moduleWrapper';
import { DownloadsModule } from '../modules';
import DownloadComponent from '../modules/downloads/DownloadsModule';

const useStyles = createStyles((theme, _params) => ({
item: {
borderBottom: 0,
overflow: 'hidden',
border: '1px solid transparent',
borderRadius: theme.radius.lg,
marginTop: theme.spacing.md,
},

itemOpened: {
borderColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[3],
},
}));

const AppShelf = (props: any) => {
const { classes, cx } = useStyles(props);
const [toggledCategories, settoggledCategories] = useLocalStorage({
key: 'app-shelf-toggled',
// This is a bit of a hack to get the 5 first categories to be toggled on by default
defaultValue: { 0: true, 1: true, 2: true, 3: true, 4: true } as Record<string, boolean>,
});
const [activeId, setActiveId] = useState(null);
const { config, setConfig } = useConfig();
const { colorScheme } = useMantineColorScheme();

const sensors = useSensors(
useSensor(TouchSensor, {}),
useSensor(MouseSensor, {
// Require the mouse to move by 10 pixels before activating
activationConstraint: {
Expand Down Expand Up @@ -99,26 +125,51 @@ const AppShelf = (props: any) => {
const noCategory = config.services.filter(
(e) => e.category === undefined || e.category === null
);

// Create an item with 0: true, 1: true, 2: true... For each category
return (
// Return one item for each category
<Group grow direction="column">
{categoryList.map((category) => (
<>
<Title order={3} key={category}>
{category}
</Title>
{item(category)}
</>
))}
{/* Return the item for all services without category */}
{noCategory && noCategory.length > 0 ? (
<>
<Title order={3}>Other</Title>
{item()}
</>
) : null}
<ModuleWrapper mt="xl" module={DownloadsModule} />
<Accordion
disableIconRotation
classNames={classes}
order={2}
iconPosition="right"
multiple
styles={{
item: {
borderRadius: '20px',
},
}}
initialState={toggledCategories}
onChange={(idx) => settoggledCategories(idx)}
>
{categoryList.map((category, idx) => (
<Accordion.Item key={category} label={category}>
{item(category)}
</Accordion.Item>
))}
{/* Return the item for all services without category */}
{noCategory && noCategory.length > 0 ? (
<Accordion.Item key="Other" label="Other">
{item()}
</Accordion.Item>
) : null}
<Accordion.Item key="Downloads" label="Your downloads">
<Paper
p="lg"
radius="lg"
style={{
background: `rgba(${colorScheme === 'dark' ? '37, 38, 43,' : '255, 255, 255,'} \
${(config.settings.appOpacity || 100) / 100}`,
borderColor: `rgba(${colorScheme === 'dark' ? '37, 38, 43,' : '233, 236, 239,'} \
${(config.settings.appOpacity || 100) / 100}`,
}}
>
<ModuleMenu module={DownloadsModule} />
<DownloadComponent />
</Paper>
</Accordion.Item>
</Accordion>
</Group>
);
}
Expand Down
Loading

0 comments on commit 778988d

Please sign in to comment.