mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-12 15:22:35 +02:00
Various updates
This commit is contained in:
@@ -39,13 +39,13 @@ export const App = () => {
|
|||||||
defaultRadius: 'xs',
|
defaultRadius: 'xs',
|
||||||
dir: 'ltr',
|
dir: 'ltr',
|
||||||
focusRing: 'never',
|
focusRing: 'never',
|
||||||
fontFamily: 'Poppins, sans-serif',
|
fontFamily: 'Sora, sans-serif',
|
||||||
fontSizes: {
|
fontSizes: {
|
||||||
lg: 16,
|
lg: 16,
|
||||||
md: 14,
|
md: 14,
|
||||||
sm: 12,
|
sm: 13,
|
||||||
xl: 18,
|
xl: 18,
|
||||||
xs: 10,
|
xs: 12,
|
||||||
},
|
},
|
||||||
other: {},
|
other: {},
|
||||||
spacing: {
|
spacing: {
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import { useEffect, useState } from 'react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import format from 'format-duration';
|
import format from 'format-duration';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { IoIosPause } from 'react-icons/io';
|
||||||
import {
|
import {
|
||||||
RiPauseLine,
|
|
||||||
RiPlayFill,
|
RiPlayFill,
|
||||||
RiRepeat2Fill,
|
RiRepeat2Fill,
|
||||||
RiShuffleFill,
|
RiShuffleFill,
|
||||||
@@ -55,7 +54,6 @@ const SliderWrapper = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
const [isSeeking, setIsSeeking] = useState(false);
|
const [isSeeking, setIsSeeking] = useState(false);
|
||||||
const playerData = usePlayerStore((state) => state.getPlayerData());
|
const playerData = usePlayerStore((state) => state.getPlayerData());
|
||||||
const player1 = playersRef?.current?.player1?.player;
|
const player1 = playersRef?.current?.player1?.player;
|
||||||
@@ -94,17 +92,17 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ControlsContainer onScroll={(e) => console.log(e)}>
|
<ControlsContainer>
|
||||||
<ButtonsContainer>
|
<ButtonsContainer>
|
||||||
<PlayerButton
|
<PlayerButton
|
||||||
icon={<RiShuffleFill size={15} />}
|
icon={<RiShuffleFill size={15} />}
|
||||||
tooltip={{ label: `${t('player.shuffle')}` }}
|
tooltip={{ label: `Shuffle`, openDelay: 500 }}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={handlePrevTrack}
|
onClick={handlePrevTrack}
|
||||||
/>
|
/>
|
||||||
<PlayerButton
|
<PlayerButton
|
||||||
icon={<RiSkipBackFill size={15} />}
|
icon={<RiSkipBackFill size={15} />}
|
||||||
tooltip={{ label: `${t('player.previous')}` }}
|
tooltip={{ label: `Previous track`, openDelay: 500 }}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={handlePrevTrack}
|
onClick={handlePrevTrack}
|
||||||
/>
|
/>
|
||||||
@@ -113,27 +111,25 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||||||
status === PlayerStatus.PAUSED ? (
|
status === PlayerStatus.PAUSED ? (
|
||||||
<RiPlayFill size={20} />
|
<RiPlayFill size={20} />
|
||||||
) : (
|
) : (
|
||||||
<RiPauseLine size={20} stroke="20px" />
|
<IoIosPause size={20} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
tooltip={{
|
tooltip={{
|
||||||
label:
|
label: status === PlayerStatus.PAUSED ? 'Play' : 'Pause',
|
||||||
status === PlayerStatus.PAUSED
|
openDelay: 500,
|
||||||
? `${t('player.play')}`
|
|
||||||
: `${t('player.pause')}`,
|
|
||||||
}}
|
}}
|
||||||
variant="main"
|
variant="main"
|
||||||
onClick={handlePlayPause}
|
onClick={handlePlayPause}
|
||||||
/>
|
/>
|
||||||
<PlayerButton
|
<PlayerButton
|
||||||
icon={<RiSkipForwardFill size={15} />}
|
icon={<RiSkipForwardFill size={15} />}
|
||||||
tooltip={{ label: `${t('player.next')}` }}
|
tooltip={{ label: 'Next track', openDelay: 500 }}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={handleNextTrack}
|
onClick={handleNextTrack}
|
||||||
/>
|
/>
|
||||||
<PlayerButton
|
<PlayerButton
|
||||||
icon={<RiRepeat2Fill size={15} />}
|
icon={<RiRepeat2Fill size={15} />}
|
||||||
tooltip={{ label: `${t('player.repeat')}` }}
|
tooltip={{ label: 'Repeat', openDelay: 500 }}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={handleNextTrack}
|
onClick={handleNextTrack}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* stylelint-disable no-descending-specificity */
|
||||||
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
@@ -7,7 +8,7 @@ import {
|
|||||||
UnstyledButtonProps,
|
UnstyledButtonProps,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Tooltip } from '../../../components';
|
import { Tooltip } from '@/renderer/components';
|
||||||
|
|
||||||
type MantineButtonProps = UnstyledButtonProps &
|
type MantineButtonProps = UnstyledButtonProps &
|
||||||
ComponentPropsWithoutRef<'button'>;
|
ComponentPropsWithoutRef<'button'>;
|
||||||
@@ -33,15 +34,24 @@ const MotionWrapper = styled(motion.div)<MotionWrapperProps>`
|
|||||||
|
|
||||||
const ButtonMainVariant = css`
|
const ButtonMainVariant = css`
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border: 2px solid var(--playerbar-btn-color);
|
background-color: var(--playerbar-btn-bg);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
fill: var(--playerbar-btn-fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
background: var(--playerbar-btn-color-hover);
|
background: var(--playerbar-btn-bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--playerbar-btn-bg-hover);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--playerbar-btn-fg-hover);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -50,21 +60,21 @@ const ButtonSecondaryVariant = css`
|
|||||||
|
|
||||||
svg {
|
svg {
|
||||||
display: flex;
|
display: flex;
|
||||||
fill: var(--playerbar-btn-color);
|
fill: var(--playerbar-btn-bg);
|
||||||
stroke: var(--playerbar-btn-color);
|
stroke: var(--playerbar-btn-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
svg {
|
svg {
|
||||||
fill: var(--playerbar-btn-color-hover);
|
fill: var(--playerbar-btn-bg-hover);
|
||||||
stroke: var(--playerbar-btn-color-hover);
|
stroke: var(--playerbar-btn-bg-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
svg {
|
svg {
|
||||||
fill: var(--playerbar-btn-color-hover);
|
fill: var(--playerbar-btn-bg-hover);
|
||||||
stroke: var(--playerbar-btn-color-hover);
|
stroke: var(--playerbar-btn-bg-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export const RightControls = () => {
|
|||||||
<Group>
|
<Group>
|
||||||
<PlayerButton
|
<PlayerButton
|
||||||
icon={<RiPlayListFill size={15} />}
|
icon={<RiPlayListFill size={15} />}
|
||||||
tooltip={{ label: 'View queue' }}
|
tooltip={{ label: 'View queue', openDelay: 500 }}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => setSidebar({ rightExpanded: !isQueueExpanded })}
|
onClick={() => setSidebar({ rightExpanded: !isQueueExpanded })}
|
||||||
/>
|
/>
|
||||||
@@ -63,7 +63,7 @@ export const RightControls = () => {
|
|||||||
<RiVolumeUpFill size={15} />
|
<RiVolumeUpFill size={15} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
tooltip={{ label: muted ? 'Muted' : volume }}
|
tooltip={{ label: muted ? 'Muted' : volume, openDelay: 500 }}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={handleMute}
|
onClick={handleMute}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Group } from '@mantine/core';
|
import { Group } from '@mantine/core';
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { FiActivity } from 'react-icons/fi';
|
import { FiActivity } from 'react-icons/fi';
|
||||||
import { RiRefreshLine } from 'react-icons/ri';
|
import { RiRefreshLine } from 'react-icons/ri';
|
||||||
import { socket } from '@/renderer/api';
|
import { socket } from '@/renderer/api';
|
||||||
|
import { queryKeys } from '@/renderer/api/query-keys';
|
||||||
import { Button, Popover, Text } from '@/renderer/components';
|
import { Button, Popover, Text } from '@/renderer/components';
|
||||||
import { useTaskList } from '@/renderer/features/tasks';
|
import { useTaskList } from '@/renderer/features/tasks';
|
||||||
|
import { useAuthStore } from '@/renderer/store';
|
||||||
import { rotating } from '@/renderer/styles';
|
import { rotating } from '@/renderer/styles';
|
||||||
|
|
||||||
const StyledActivitySvg = styled(RiRefreshLine)`
|
const StyledActivitySvg = styled(RiRefreshLine)`
|
||||||
@@ -14,10 +17,13 @@ const StyledActivitySvg = styled(RiRefreshLine)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const ActivityMenu = () => {
|
export const ActivityMenu = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const serverId = useAuthStore((state) => state.currentServer?.id) || '';
|
||||||
const [isTaskRunning, setIsTaskRunning] = useState(false);
|
const [isTaskRunning, setIsTaskRunning] = useState(false);
|
||||||
const { data: tasks, refetch } = useTaskList({
|
const { data: tasks, refetch } = useTaskList({
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (data.data.length === 0) {
|
if (data.data.length === 0) {
|
||||||
|
queryClient.invalidateQueries(queryKeys.server.root(serverId));
|
||||||
return setIsTaskRunning(false);
|
return setIsTaskRunning(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,14 +69,9 @@ export const ActivityMenu = () => {
|
|||||||
<>
|
<>
|
||||||
<Popover withArrow withinPortal>
|
<Popover withArrow withinPortal>
|
||||||
<Popover.Target>
|
<Popover.Target>
|
||||||
<Button
|
<Button px={5} size="xs" variant="subtle">
|
||||||
px={5}
|
|
||||||
size="xs"
|
|
||||||
sx={{ color: 'var(--titlebar-fg)' }}
|
|
||||||
variant="subtle"
|
|
||||||
>
|
|
||||||
{isTaskRunning ? (
|
{isTaskRunning ? (
|
||||||
<StyledActivitySvg size={15} />
|
<StyledActivitySvg color="var(--primary-color)" size={15} />
|
||||||
) : (
|
) : (
|
||||||
<FiActivity size={15} />
|
<FiActivity size={15} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { Group } from '@mantine/core';
|
import { Group } from '@mantine/core';
|
||||||
import { openModal, closeAllModals } from '@mantine/modals';
|
import { openModal, closeAllModals } from '@mantine/modals';
|
||||||
import {
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
RiArrowLeftLine,
|
import { RiLock2Line, RiLogoutBoxLine, RiMenu3Fill } from 'react-icons/ri';
|
||||||
RiLock2Line,
|
|
||||||
RiLogoutBoxLine,
|
|
||||||
RiMenu3Fill,
|
|
||||||
} from 'react-icons/ri';
|
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
|
import { queryKeys } from '@/renderer/api/query-keys';
|
||||||
import { Button, DropdownMenu } from '@/renderer/components';
|
import { Button, DropdownMenu } from '@/renderer/components';
|
||||||
import {
|
import {
|
||||||
AddServerForm,
|
AddServerForm,
|
||||||
@@ -17,6 +14,7 @@ import { usePermissions } from '@/renderer/features/shared';
|
|||||||
import { useAuthStore } from '@/renderer/store';
|
import { useAuthStore } from '@/renderer/store';
|
||||||
|
|
||||||
export const AppMenu = () => {
|
export const AppMenu = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const logout = useAuthStore((state) => state.logout);
|
const logout = useAuthStore((state) => state.logout);
|
||||||
const currentServer = useAuthStore((state) => state.currentServer);
|
const currentServer = useAuthStore((state) => state.currentServer);
|
||||||
@@ -57,6 +55,7 @@ export const AppMenu = () => {
|
|||||||
const server = servers?.data.find((s) => s.id === serverId);
|
const server = servers?.data.find((s) => s.id === serverId);
|
||||||
if (!server) return;
|
if (!server) return;
|
||||||
setCurrentServer(server);
|
setCurrentServer(server);
|
||||||
|
queryClient.invalidateQueries(queryKeys.server.root(serverId));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -82,15 +81,7 @@ export const AppMenu = () => {
|
|||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
key={`server-${s.id}`}
|
key={`server-${s.id}`}
|
||||||
disabled={requiresCredential}
|
disabled={requiresCredential}
|
||||||
rightSection={
|
isActive={s.id === currentServer?.id}
|
||||||
s.id === currentServer?.id ? <RiArrowLeftLine /> : undefined
|
|
||||||
}
|
|
||||||
sx={{
|
|
||||||
color:
|
|
||||||
s.id === currentServer?.id
|
|
||||||
? 'var(--primary-color)'
|
|
||||||
: undefined,
|
|
||||||
}}
|
|
||||||
onClick={() => handleSetCurrentServer(s.id)}
|
onClick={() => handleSetCurrentServer(s.id)}
|
||||||
>
|
>
|
||||||
<Group>
|
<Group>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { I18nextProvider } from 'react-i18next';
|
import { I18nextProvider } from 'react-i18next';
|
||||||
import { queryClient } from '@/renderer/lib/react-query';
|
import { queryClient } from '@/renderer/lib/react-query';
|
||||||
@@ -12,6 +13,7 @@ root.render(
|
|||||||
<I18nextProvider i18n={i18n}>
|
<I18nextProvider i18n={i18n}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<App />
|
<App />
|
||||||
|
<ReactQueryDevtools position="bottom-left" />
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ const PlayerbarContainer = styled.footer`
|
|||||||
z-index: 50;
|
z-index: 50;
|
||||||
grid-area: player;
|
grid-area: player;
|
||||||
background: var(--playerbar-bg);
|
background: var(--playerbar-bg);
|
||||||
|
filter: drop-shadow(0 -3px 4mm rgba(0, 0, 0, 50%));
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ResizeHandle = styled.div<{
|
const ResizeHandle = styled.div<{
|
||||||
@@ -108,7 +109,7 @@ export const DefaultLayout = ({ shell }: DefaultLayoutProps) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const resize = useCallback(
|
const resize = useCallback(
|
||||||
(mouseMoveEvent) => {
|
(mouseMoveEvent: any) => {
|
||||||
if (isResizing) {
|
if (isResizing) {
|
||||||
const width = `${constrainSidebarWidth(mouseMoveEvent.clientX)}px`;
|
const width = `${constrainSidebarWidth(mouseMoveEvent.clientX)}px`;
|
||||||
setSidebar({ leftWidth: width });
|
setSidebar({ leftWidth: width });
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ type TArgs =
|
|||||||
| { path: AppRoute.LIBRARY_ARTISTS }
|
| { path: AppRoute.LIBRARY_ARTISTS }
|
||||||
| { path: AppRoute.LIBRARY_ARTISTS_DETAIL }
|
| { path: AppRoute.LIBRARY_ARTISTS_DETAIL }
|
||||||
| { path: AppRoute.LIBRARY_ALBUMARTISTS }
|
| { path: AppRoute.LIBRARY_ALBUMARTISTS }
|
||||||
| { path: AppRoute.LIBRARY_ALBUMARTISTS_DETAIL }
|
| {
|
||||||
|
params: { albumArtistId: string };
|
||||||
|
path: AppRoute.LIBRARY_ALBUMARTISTS_DETAIL;
|
||||||
|
}
|
||||||
| { path: AppRoute.LIBRARY_ALBUMS }
|
| { path: AppRoute.LIBRARY_ALBUMS }
|
||||||
| { path: AppRoute.LIBRARY_FOLDERS }
|
| { path: AppRoute.LIBRARY_FOLDERS }
|
||||||
| { path: AppRoute.LIBRARY_SONGS }
|
| { path: AppRoute.LIBRARY_SONGS }
|
||||||
|
|||||||
Reference in New Issue
Block a user