mirror of
https://github.com/antebudimir/feishin.git
synced 2025-12-31 18:13:31 +00:00
Migrate to Mantine v8 and Design Changes (#961)
* mantine v8 migration * various design changes and improvements
This commit is contained in:
parent
bea55d48a8
commit
c1330d92b2
473 changed files with 12469 additions and 11607 deletions
|
|
@ -1,4 +1,3 @@
|
|||
import { Checkbox, Group, Stack } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useFocusTrap } from '@mantine/hooks';
|
||||
import { closeAllModals } from '@mantine/modals';
|
||||
|
|
@ -8,23 +7,74 @@ import { useState } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { Button, PasswordInput, SegmentedControl, TextInput, toast } from '/@/renderer/components';
|
||||
import JellyfinIcon from '/@/renderer/features/servers/assets/jellyfin.png';
|
||||
import NavidromeIcon from '/@/renderer/features/servers/assets/navidrome.png';
|
||||
import SubsonicIcon from '/@/renderer/features/servers/assets/opensubsonic.png';
|
||||
import { useAuthStoreActions } from '/@/renderer/store';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Checkbox } from '/@/shared/components/checkbox/checkbox';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { PasswordInput } from '/@/shared/components/password-input/password-input';
|
||||
import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { TextInput } from '/@/shared/components/text-input/text-input';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { toast } from '/@/shared/components/toast/toast';
|
||||
import { AuthenticationResponse } from '/@/shared/types/domain-types';
|
||||
import { ServerType, toServerType } from '/@/shared/types/types';
|
||||
|
||||
const localSettings = isElectron() ? window.api.localSettings : null;
|
||||
|
||||
const SERVER_TYPES = [
|
||||
{ label: 'Jellyfin', value: ServerType.JELLYFIN },
|
||||
{ label: 'Navidrome', value: ServerType.NAVIDROME },
|
||||
{ label: 'Subsonic', value: ServerType.SUBSONIC },
|
||||
];
|
||||
|
||||
interface AddServerFormProps {
|
||||
onCancel: () => void;
|
||||
onCancel: (() => void) | null;
|
||||
}
|
||||
|
||||
function ServerIconWithLabel({ icon, label }: { icon: string; label: string }) {
|
||||
return (
|
||||
<Stack
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<img
|
||||
height="50"
|
||||
src={icon}
|
||||
width="50"
|
||||
/>
|
||||
<Text>{label}</Text>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const SERVER_TYPES = [
|
||||
{
|
||||
label: (
|
||||
<ServerIconWithLabel
|
||||
icon={JellyfinIcon}
|
||||
label="Jellyfin"
|
||||
/>
|
||||
),
|
||||
value: ServerType.JELLYFIN,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<ServerIconWithLabel
|
||||
icon={NavidromeIcon}
|
||||
label="Navidrome"
|
||||
/>
|
||||
),
|
||||
value: ServerType.NAVIDROME,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<ServerIconWithLabel
|
||||
icon={SubsonicIcon}
|
||||
label="OpenSubsonic"
|
||||
/>
|
||||
),
|
||||
value: ServerType.SUBSONIC,
|
||||
},
|
||||
];
|
||||
|
||||
export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
||||
const { t } = useTranslation();
|
||||
const focusTrapRef = useFocusTrap(true);
|
||||
|
|
@ -40,7 +90,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
|||
type:
|
||||
(localSettings
|
||||
? localSettings.env.SERVER_TYPE
|
||||
: toServerType(window.SERVER_TYPE)) ?? ServerType.JELLYFIN,
|
||||
: toServerType(window.SERVER_TYPE)) ?? ServerType.NAVIDROME,
|
||||
url: (localSettings ? localSettings.env.SERVER_URL : window.SERVER_URL) ?? 'https://',
|
||||
username: '',
|
||||
},
|
||||
|
|
@ -131,6 +181,8 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
|||
<SegmentedControl
|
||||
data={SERVER_TYPES}
|
||||
disabled={Boolean(serverLock)}
|
||||
p="md"
|
||||
withItemsBorders={false}
|
||||
{...form.getInputProps('type')}
|
||||
/>
|
||||
<Group grow>
|
||||
|
|
@ -186,13 +238,18 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
|||
{...form.getInputProps('legacyAuth', { type: 'checkbox' })}
|
||||
/>
|
||||
)}
|
||||
<Group position="right">
|
||||
<Button
|
||||
onClick={onCancel}
|
||||
variant="subtle"
|
||||
>
|
||||
{t('common.cancel', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
<Group
|
||||
grow
|
||||
justify="flex-end"
|
||||
>
|
||||
{onCancel && (
|
||||
<Button
|
||||
onClick={onCancel}
|
||||
variant="subtle"
|
||||
>
|
||||
{t('common.cancel', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
disabled={isSubmitDisabled}
|
||||
loading={isLoading}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,24 @@
|
|||
import { Group, Stack } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useFocusTrap } from '@mantine/hooks';
|
||||
import { closeAllModals } from '@mantine/modals';
|
||||
import isElectron from 'is-electron';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiInformationLine } from 'react-icons/ri';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { Button, Checkbox, PasswordInput, TextInput, toast, Tooltip } from '/@/renderer/components';
|
||||
import { queryClient } from '/@/renderer/lib/react-query';
|
||||
import { useAuthStoreActions } from '/@/renderer/store';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Checkbox } from '/@/shared/components/checkbox/checkbox';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { PasswordInput } from '/@/shared/components/password-input/password-input';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { TextInput } from '/@/shared/components/text-input/text-input';
|
||||
import { toast } from '/@/shared/components/toast/toast';
|
||||
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
|
||||
import { AuthenticationResponse, ServerListItem, ServerType } from '/@/shared/types/domain-types';
|
||||
|
||||
const localSettings = isElectron() ? window.api.localSettings : null;
|
||||
|
|
@ -27,9 +33,10 @@ interface EditServerFormProps {
|
|||
const ModifiedFieldIndicator = () => {
|
||||
return (
|
||||
<Tooltip label={i18n.t('common.modified', { postProcess: 'titleCase' }) as string}>
|
||||
<span>
|
||||
<RiInformationLine color="red" />
|
||||
</span>
|
||||
<Icon
|
||||
color="warn"
|
||||
icon="info"
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
|
@ -185,7 +192,7 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
|
|||
})}
|
||||
/>
|
||||
)}
|
||||
<Group position="right">
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
onClick={onCancel}
|
||||
variant="subtle"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
import { Divider, Group, Stack } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import isElectron from 'is-electron';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiDeleteBin2Line, RiEdit2Fill } from 'react-icons/ri';
|
||||
|
||||
import { Button, Text, TimeoutButton } from '/@/renderer/components';
|
||||
import { EditServerForm } from '/@/renderer/features/servers/components/edit-server-form';
|
||||
import { ServerSection } from '/@/renderer/features/servers/components/server-section';
|
||||
import { useAuthStoreActions } from '/@/renderer/store';
|
||||
import { Button, TimeoutButton } from '/@/shared/components/button/button';
|
||||
import { Divider } from '/@/shared/components/divider/divider';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { Table } from '/@/shared/components/table/table';
|
||||
import { ServerListItem as ServerItem } from '/@/shared/types/domain-types';
|
||||
|
||||
const localSettings = isElectron() ? window.api.localSettings : null;
|
||||
|
|
@ -54,17 +57,7 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
|||
|
||||
return (
|
||||
<Stack>
|
||||
<ServerSection
|
||||
title={
|
||||
<Group position="apart">
|
||||
<Text>
|
||||
{t('page.manageServers.serverDetails', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Text>
|
||||
</Group>
|
||||
}
|
||||
>
|
||||
<ServerSection title={null}>
|
||||
{edit ? (
|
||||
<EditServerForm
|
||||
onCancel={() => editHandlers.toggle()}
|
||||
|
|
@ -73,36 +66,41 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
|||
/>
|
||||
) : (
|
||||
<Stack>
|
||||
<Group noWrap>
|
||||
<Stack>
|
||||
<Text>
|
||||
{t('page.manageServers.url', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Text>
|
||||
<Text>
|
||||
{t('page.manageServers.username', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>{server.url}</Text>
|
||||
<Text>{server.username}</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
<Table
|
||||
layout="fixed"
|
||||
variant="vertical"
|
||||
withTableBorder
|
||||
>
|
||||
<Table.Tbody>
|
||||
<Table.Tr>
|
||||
<Table.Th>
|
||||
{t('page.manageServers.url', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Table.Th>
|
||||
<Table.Td>{server.url}</Table.Td>
|
||||
</Table.Tr>
|
||||
<Table.Tr>
|
||||
<Table.Th>
|
||||
{t('page.manageServers.username', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Table.Th>
|
||||
<Table.Td>{server.username}</Table.Td>
|
||||
</Table.Tr>
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
<Group grow>
|
||||
<Button
|
||||
leftIcon={<RiEdit2Fill />}
|
||||
leftSection={<Icon icon="edit" />}
|
||||
onClick={() => handleEdit()}
|
||||
tooltip={{
|
||||
label: t('page.manageServers.editServerDetailsTooltip', {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
}}
|
||||
variant="subtle"
|
||||
>
|
||||
{t('common.edit')}
|
||||
{t('common.edit', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
|
@ -110,9 +108,9 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
|||
</ServerSection>
|
||||
<Divider my="sm" />
|
||||
<TimeoutButton
|
||||
leftIcon={<RiDeleteBin2Line />}
|
||||
leftSection={<Icon icon="delete" />}
|
||||
timeoutProps={{ callback: handleDeleteServer, duration: 1000 }}
|
||||
variant="subtle"
|
||||
variant="state-error"
|
||||
>
|
||||
{t('page.manageServers.removeServer', { postProcess: 'sentenceCase' })}
|
||||
</TimeoutButton>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,25 @@
|
|||
import { Divider, Group, Stack } from '@mantine/core';
|
||||
import { useLocalStorage } from '@mantine/hooks';
|
||||
import { openContextModal } from '@mantine/modals';
|
||||
import isElectron from 'is-electron';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiAddFill, RiServerFill } from 'react-icons/ri';
|
||||
|
||||
import { Accordion, Button, ContextModalVars, Switch, Text } from '/@/renderer/components';
|
||||
import JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png';
|
||||
import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png';
|
||||
import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png';
|
||||
import { AddServerForm } from '/@/renderer/features/servers/components/add-server-form';
|
||||
import { ServerListItem } from '/@/renderer/features/servers/components/server-list-item';
|
||||
import { useCurrentServer, useServerList } from '/@/renderer/store';
|
||||
import { titleCase } from '/@/renderer/utils';
|
||||
import { Accordion } from '/@/shared/components/accordion/accordion';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Divider } from '/@/shared/components/divider/divider';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { ContextModalVars } from '/@/shared/components/modal/modal';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { Switch } from '/@/shared/components/switch/switch';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { ServerType } from '/@/shared/types/domain-types';
|
||||
|
||||
const localSettings = isElectron() ? window.api.localSettings : null;
|
||||
|
||||
|
|
@ -59,27 +68,6 @@ export const ServerList = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Group
|
||||
mb={10}
|
||||
position="right"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
right: 55,
|
||||
transform: 'translateY(-3.5rem)',
|
||||
zIndex: 2000,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
autoFocus
|
||||
compact
|
||||
leftIcon={<RiAddFill size={15} />}
|
||||
onClick={handleAddServerModal}
|
||||
size="sm"
|
||||
variant="filled"
|
||||
>
|
||||
{t('form.addServer.title', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
<Stack>
|
||||
<Accordion variant="separated">
|
||||
{Object.keys(serverListQuery)?.map((serverId) => {
|
||||
|
|
@ -89,10 +77,23 @@ export const ServerList = () => {
|
|||
key={server.id}
|
||||
value={server.name}
|
||||
>
|
||||
<Accordion.Control icon={<RiServerFill size={15} />}>
|
||||
<Group position="apart">
|
||||
<Text weight={server.id === currentServer?.id ? 800 : 400}>
|
||||
{titleCase(server?.type)} - {server?.name}
|
||||
<Accordion.Control>
|
||||
<Group>
|
||||
<img
|
||||
src={
|
||||
server.type === ServerType.NAVIDROME
|
||||
? NavidromeLogo
|
||||
: server.type === ServerType.JELLYFIN
|
||||
? JellyfinLogo
|
||||
: OpenSubsonicLogo
|
||||
}
|
||||
style={{
|
||||
height: 'var(--theme-font-size-lg)',
|
||||
width: 'var(--theme-font-size-lg)',
|
||||
}}
|
||||
/>
|
||||
<Text fw={server.id === currentServer?.id ? 600 : 400}>
|
||||
{server?.name}
|
||||
</Text>
|
||||
</Group>
|
||||
</Accordion.Control>
|
||||
|
|
@ -102,6 +103,18 @@ export const ServerList = () => {
|
|||
</Accordion.Item>
|
||||
);
|
||||
})}
|
||||
<Group
|
||||
grow
|
||||
pt="md"
|
||||
>
|
||||
<Button
|
||||
autoFocus
|
||||
leftSection={<Icon icon="add" />}
|
||||
onClick={handleAddServerModal}
|
||||
>
|
||||
{t('form.addServer.title', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Accordion>
|
||||
{isElectron() && (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,17 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import React, { Fragment } from 'react';
|
||||
|
||||
import { Text } from '/@/renderer/components';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
|
||||
interface ServerSectionProps {
|
||||
children: React.ReactNode;
|
||||
title: React.ReactNode | string;
|
||||
}
|
||||
|
||||
const Container = styled.div``;
|
||||
|
||||
const Section = styled.div`
|
||||
padding: 1rem;
|
||||
border: 1px dashed var(--generic-border-color);
|
||||
`;
|
||||
|
||||
export const ServerSection = ({ children, title }: ServerSectionProps) => {
|
||||
return (
|
||||
<Container>
|
||||
<Fragment>
|
||||
{React.isValidElement(title) ? title : <Text>{title}</Text>}
|
||||
<Section>{children}</Section>
|
||||
</Container>
|
||||
<div style={{ padding: '1rem' }}>{children}</div>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue