mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
Update player/shared components
This commit is contained in:
+28
-34
@@ -1,20 +1,14 @@
|
|||||||
import { ReactNode, useEffect } from 'react';
|
import { ReactNode, useEffect } from 'react';
|
||||||
import {
|
|
||||||
DndContext,
|
|
||||||
MouseSensor,
|
|
||||||
TouchSensor,
|
|
||||||
useSensor,
|
|
||||||
useSensors,
|
|
||||||
} from '@dnd-kit/core';
|
|
||||||
import { MantineProvider } from '@mantine/core';
|
import { MantineProvider } from '@mantine/core';
|
||||||
import { useLocalStorage } from '@mantine/hooks';
|
import { useLocalStorage } from '@mantine/hooks';
|
||||||
|
import { ModalsProvider } from '@mantine/modals';
|
||||||
|
import { NotificationsProvider } from '@mantine/notifications';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { BrowserRouter, HashRouter } from 'react-router-dom';
|
import { BrowserRouter, HashRouter } from 'react-router-dom';
|
||||||
import { useDefaultSettings } from './features/settings';
|
import { useDefaultSettings } from './features/settings';
|
||||||
import { AppRouter } from './router/AppRouter';
|
import { AppRouter } from './router/app-router';
|
||||||
import './styles/global.scss';
|
import './styles/global.scss';
|
||||||
import 'ag-grid-community/styles/ag-grid.css';
|
import 'ag-grid-community/styles/ag-grid.css';
|
||||||
import './styles/ag-grid.scss';
|
|
||||||
|
|
||||||
const SelectRouter = ({ children }: { children: ReactNode }) => {
|
const SelectRouter = ({ children }: { children: ReactNode }) => {
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
@@ -36,27 +30,16 @@ export const App = () => {
|
|||||||
document.body.setAttribute('data-theme', theme);
|
document.body.setAttribute('data-theme', theme);
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
const sensors = useSensors(
|
|
||||||
useSensor(MouseSensor, {
|
|
||||||
activationConstraint: {
|
|
||||||
delay: 200,
|
|
||||||
tolerance: 100,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
useSensor(TouchSensor, {
|
|
||||||
activationConstraint: {
|
|
||||||
delay: 500,
|
|
||||||
tolerance: 10,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MantineProvider
|
<MantineProvider
|
||||||
|
withGlobalStyles
|
||||||
|
withNormalizeCSS
|
||||||
theme={{
|
theme={{
|
||||||
colorScheme: 'dark',
|
colorScheme: 'dark',
|
||||||
defaultRadius: 'xs',
|
defaultRadius: 'xs',
|
||||||
focusRing: 'auto',
|
dir: 'ltr',
|
||||||
|
focusRing: 'never',
|
||||||
|
fontFamily: 'Poppins, sans-serif',
|
||||||
fontSizes: {
|
fontSizes: {
|
||||||
lg: 16,
|
lg: 16,
|
||||||
md: 14,
|
md: 14,
|
||||||
@@ -64,22 +47,33 @@ export const App = () => {
|
|||||||
xl: 18,
|
xl: 18,
|
||||||
xs: 10,
|
xs: 10,
|
||||||
},
|
},
|
||||||
|
|
||||||
other: {},
|
other: {},
|
||||||
spacing: {
|
spacing: {
|
||||||
|
lg: 12,
|
||||||
|
md: 8,
|
||||||
|
sm: 4,
|
||||||
|
xl: 16,
|
||||||
xs: 2,
|
xs: 2,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DndContext
|
<NotificationsProvider
|
||||||
sensors={sensors}
|
autoClose={1500}
|
||||||
onDragEnd={() => console.log('drag end')}
|
position="bottom-right"
|
||||||
onDragStart={() => console.log('drag start')}
|
style={{
|
||||||
|
marginBottom: '90px',
|
||||||
|
opacity: '.8',
|
||||||
|
userSelect: 'none',
|
||||||
|
width: '250px',
|
||||||
|
}}
|
||||||
|
transitionDuration={200}
|
||||||
>
|
>
|
||||||
<SelectRouter>
|
<ModalsProvider>
|
||||||
<AppRouter />
|
<SelectRouter>
|
||||||
</SelectRouter>
|
<AppRouter />
|
||||||
</DndContext>
|
</SelectRouter>
|
||||||
|
</ModalsProvider>
|
||||||
|
</NotificationsProvider>
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Ref, useMemo } from 'react';
|
import { Ref, useMemo } from 'react';
|
||||||
import { FixedSizeList, FixedSizeListProps } from 'react-window';
|
import { FixedSizeList, FixedSizeListProps } from 'react-window';
|
||||||
import { GridCard } from '@/renderer/components/virtual-grid/grid-card';
|
import { GridCard } from '@/renderer/components/virtual-grid/grid-card';
|
||||||
import { usePlayQueueHandler } from '@/renderer/features/player/hooks/usePlayQueueHandler';
|
import { usePlayQueueHandler } from '@/renderer/features/player/hooks/use-playqueue-handler';
|
||||||
import { CardRow } from '@/renderer/types';
|
import { CardRow } from '@/renderer/types';
|
||||||
|
|
||||||
export const VirtualGridWrapper = ({
|
export const VirtualGridWrapper = ({
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from '@emotion/styled';
|
||||||
import { PlaybackType } from '../../../../types';
|
import { PlaybackType } from '../../../../types';
|
||||||
import { AudioPlayer } from '../../../components';
|
import { AudioPlayer } from '../../../components';
|
||||||
import { usePlayerStore } from '../../../store';
|
import { usePlayerStore } from '../../../store';
|
||||||
import { CenterControls } from './CenterControls';
|
import { CenterControls } from './center-controls';
|
||||||
import { LeftControls } from './LeftControls';
|
import { LeftControls } from './left-controls';
|
||||||
import { RightControls } from './RightControls';
|
import { RightControls } from './right-controls';
|
||||||
|
|
||||||
const PlayerbarContainer = styled.div`
|
const PlayerbarContainer = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
import format from 'format-duration';
|
import format from 'format-duration';
|
||||||
import ReactSlider, { ReactSliderProps } from 'react-slider';
|
import ReactSlider, { ReactSliderProps } from 'react-slider';
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
interface SliderProps extends ReactSliderProps {
|
interface SliderProps extends ReactSliderProps {
|
||||||
hasTooltip?: boolean;
|
hasTooltip?: boolean;
|
||||||
|
|||||||
+17
-19
@@ -1,22 +1,22 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
import format from 'format-duration';
|
import format from 'format-duration';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
RiPauseLine,
|
RiPauseLine,
|
||||||
RiPlayFill,
|
RiPlayFill,
|
||||||
RiRewindFill,
|
RiRepeat2Fill,
|
||||||
|
RiShuffleFill,
|
||||||
RiSkipBackFill,
|
RiSkipBackFill,
|
||||||
RiSkipForwardFill,
|
RiSkipForwardFill,
|
||||||
RiSpeedFill,
|
|
||||||
} from 'react-icons/ri';
|
} from 'react-icons/ri';
|
||||||
import styled from 'styled-components';
|
|
||||||
import { PlaybackType, PlayerStatus } from '../../../../types';
|
import { PlaybackType, PlayerStatus } from '../../../../types';
|
||||||
import { Text } from '../../../components';
|
import { Text } from '../../../components';
|
||||||
import { usePlayerStore } from '../../../store';
|
import { usePlayerStore } from '../../../store';
|
||||||
import { Font } from '../../../styles';
|
import { Font } from '../../../styles';
|
||||||
import { useCenterControls } from '../hooks/useCenterControls';
|
import { useCenterControls } from '../hooks/use-center-controls';
|
||||||
import { PlayerButton } from './PlayerButton';
|
import { PlayerButton } from './player-button';
|
||||||
import { Slider } from './Slider';
|
import { Slider } from './slider';
|
||||||
|
|
||||||
interface CenterControlsProps {
|
interface CenterControlsProps {
|
||||||
playersRef: any;
|
playersRef: any;
|
||||||
@@ -67,8 +67,6 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||||||
handlePlayPause,
|
handlePlayPause,
|
||||||
handlePrevTrack,
|
handlePrevTrack,
|
||||||
handleSeekSlider,
|
handleSeekSlider,
|
||||||
handleSkipBackward,
|
|
||||||
handleSkipForward,
|
|
||||||
} = useCenterControls({ playersRef });
|
} = useCenterControls({ playersRef });
|
||||||
|
|
||||||
const currentTime = usePlayerStore((state) => state.current.time);
|
const currentTime = usePlayerStore((state) => state.current.time);
|
||||||
@@ -106,16 +104,16 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||||||
<ControlsContainer onScroll={(e) => console.log(e)}>
|
<ControlsContainer onScroll={(e) => console.log(e)}>
|
||||||
<ButtonsContainer>
|
<ButtonsContainer>
|
||||||
<PlayerButton
|
<PlayerButton
|
||||||
icon={<RiSkipBackFill size={15} />}
|
icon={<RiShuffleFill size={15} />}
|
||||||
tooltip={{ label: `${t('player.prev')}` }}
|
tooltip={{ label: `${t('player.shuffle')}` }}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={handlePrevTrack}
|
onClick={handlePrevTrack}
|
||||||
/>
|
/>
|
||||||
<PlayerButton
|
<PlayerButton
|
||||||
icon={<RiRewindFill size={15} />}
|
icon={<RiSkipBackFill size={15} />}
|
||||||
tooltip={{ label: `${t('player.skipBack')}` }}
|
tooltip={{ label: `${t('player.previous')}` }}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={handleSkipBackward}
|
onClick={handlePrevTrack}
|
||||||
/>
|
/>
|
||||||
<PlayerButton
|
<PlayerButton
|
||||||
icon={
|
icon={
|
||||||
@@ -134,18 +132,18 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||||||
variant="main"
|
variant="main"
|
||||||
onClick={handlePlayPause}
|
onClick={handlePlayPause}
|
||||||
/>
|
/>
|
||||||
<PlayerButton
|
|
||||||
icon={<RiSpeedFill size={15} />}
|
|
||||||
tooltip={{ label: `${t('player.skipForward')}` }}
|
|
||||||
variant="secondary"
|
|
||||||
onClick={handleSkipForward}
|
|
||||||
/>
|
|
||||||
<PlayerButton
|
<PlayerButton
|
||||||
icon={<RiSkipForwardFill size={15} />}
|
icon={<RiSkipForwardFill size={15} />}
|
||||||
tooltip={{ label: `${t('player.next')}` }}
|
tooltip={{ label: `${t('player.next')}` }}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={handleNextTrack}
|
onClick={handleNextTrack}
|
||||||
/>
|
/>
|
||||||
|
<PlayerButton
|
||||||
|
icon={<RiRepeat2Fill size={15} />}
|
||||||
|
tooltip={{ label: `${t('player.repeat')}` }}
|
||||||
|
variant="secondary"
|
||||||
|
onClick={handleNextTrack}
|
||||||
|
/>
|
||||||
</ButtonsContainer>
|
</ButtonsContainer>
|
||||||
</ControlsContainer>
|
</ControlsContainer>
|
||||||
<SliderContainer>
|
<SliderContainer>
|
||||||
+4
-2
@@ -1,4 +1,4 @@
|
|||||||
import styled from 'styled-components';
|
import styled from '@emotion/styled';
|
||||||
import { Text } from '../../../components';
|
import { Text } from '../../../components';
|
||||||
import { usePlayerStore } from '../../../store';
|
import { usePlayerStore } from '../../../store';
|
||||||
import { Font } from '../../../styles';
|
import { Font } from '../../../styles';
|
||||||
@@ -34,7 +34,9 @@ export const LeftControls = () => {
|
|||||||
return (
|
return (
|
||||||
<LeftControlsContainer>
|
<LeftControlsContainer>
|
||||||
<ImageWrapper>
|
<ImageWrapper>
|
||||||
<img alt="img" height={60} src={song?.imageUrl} width={60} />
|
{song?.imageUrl && (
|
||||||
|
<img alt="img" height={60} src={song?.imageUrl} width={60} />
|
||||||
|
)}
|
||||||
</ImageWrapper>
|
</ImageWrapper>
|
||||||
<MetadataStack>
|
<MetadataStack>
|
||||||
<Text
|
<Text
|
||||||
+11
-12
@@ -1,11 +1,12 @@
|
|||||||
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
||||||
|
import { css } from '@emotion/react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
import {
|
import {
|
||||||
TooltipProps,
|
TooltipProps,
|
||||||
UnstyledButton,
|
UnstyledButton,
|
||||||
UnstyledButtonProps,
|
UnstyledButtonProps,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import styled, { css } from 'styled-components';
|
|
||||||
import { Tooltip } from '../../../components';
|
import { Tooltip } from '../../../components';
|
||||||
|
|
||||||
type MantineButtonProps = UnstyledButtonProps &
|
type MantineButtonProps = UnstyledButtonProps &
|
||||||
@@ -32,17 +33,11 @@ const MotionWrapper = styled(motion.div)<MotionWrapperProps>`
|
|||||||
|
|
||||||
const ButtonMainVariant = css`
|
const ButtonMainVariant = css`
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
background: var(--playerbar-btn-color);
|
border: 2px solid var(--playerbar-btn-color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
display: flex;
|
display: flex;
|
||||||
fill: black;
|
|
||||||
stroke: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--playerbar-btn-color-hover);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
@@ -109,13 +104,17 @@ export const PlayerButton = ({
|
|||||||
}: PlayerButtonProps) => {
|
}: PlayerButtonProps) => {
|
||||||
if (tooltip) {
|
if (tooltip) {
|
||||||
return (
|
return (
|
||||||
<MotionWrapper variant={variant}>
|
<Tooltip {...tooltip}>
|
||||||
<Tooltip {...tooltip}>
|
<MotionWrapper
|
||||||
|
variant={variant}
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
>
|
||||||
<StyledPlayerButton variant={variant} {...rest}>
|
<StyledPlayerButton variant={variant} {...rest}>
|
||||||
{icon}
|
{icon}
|
||||||
</StyledPlayerButton>
|
</StyledPlayerButton>
|
||||||
</Tooltip>
|
</MotionWrapper>
|
||||||
</MotionWrapper>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
+9
-11
@@ -1,9 +1,9 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
import { RiVolumeUpFill, RiVolumeMuteFill } from 'react-icons/ri';
|
import { RiVolumeUpFill, RiVolumeMuteFill } from 'react-icons/ri';
|
||||||
import styled from 'styled-components';
|
|
||||||
import { IconButton } from '../../../components';
|
|
||||||
import { usePlayerStore } from '../../../store';
|
import { usePlayerStore } from '../../../store';
|
||||||
import { useRightControls } from '../hooks/useRightControls';
|
import { useRightControls } from '../hooks/use-right-controls';
|
||||||
import { Slider } from './Slider';
|
import { PlayerButton } from './player-button';
|
||||||
|
import { Slider } from './slider';
|
||||||
|
|
||||||
const RightControlsContainer = styled.div`
|
const RightControlsContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -39,23 +39,21 @@ export const RightControls = () => {
|
|||||||
<RightControlsContainer>
|
<RightControlsContainer>
|
||||||
<MetadataStack>
|
<MetadataStack>
|
||||||
<VolumeSliderWrapper>
|
<VolumeSliderWrapper>
|
||||||
<IconButton
|
<PlayerButton
|
||||||
icon={
|
icon={
|
||||||
muted ? (
|
muted ? (
|
||||||
<RiVolumeMuteFill size={20} />
|
<RiVolumeMuteFill size={15} />
|
||||||
) : (
|
) : (
|
||||||
<RiVolumeUpFill size={20} />
|
<RiVolumeUpFill size={15} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
size={20}
|
|
||||||
tooltip={{ label: muted ? 'Muted' : volume }}
|
tooltip={{ label: muted ? 'Muted' : volume }}
|
||||||
variant="transparent"
|
variant="secondary"
|
||||||
onClick={handleMute}
|
onClick={handleMute}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Slider
|
<Slider
|
||||||
hasTooltip
|
hasTooltip
|
||||||
height="100%"
|
height="60%"
|
||||||
max={100}
|
max={100}
|
||||||
min={0}
|
min={0}
|
||||||
value={volume}
|
value={volume}
|
||||||
+9
-9
@@ -42,7 +42,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handlePlay = useCallback(() => {
|
const handlePlay = useCallback(() => {
|
||||||
if (settings.type === PlaybackType.Local) {
|
if (settings.type === PlaybackType.LOCAL) {
|
||||||
mpvPlayer.play();
|
mpvPlayer.play();
|
||||||
} else {
|
} else {
|
||||||
currentPlayerRef.getInternalPlayer().play();
|
currentPlayerRef.getInternalPlayer().play();
|
||||||
@@ -52,7 +52,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
}, [currentPlayerRef, play, settings]);
|
}, [currentPlayerRef, play, settings]);
|
||||||
|
|
||||||
const handlePause = useCallback(() => {
|
const handlePause = useCallback(() => {
|
||||||
if (settings.type === PlaybackType.Local) {
|
if (settings.type === PlaybackType.LOCAL) {
|
||||||
mpvPlayer.pause();
|
mpvPlayer.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
}, [pause, settings]);
|
}, [pause, settings]);
|
||||||
|
|
||||||
const handleStop = () => {
|
const handleStop = () => {
|
||||||
if (settings.type === PlaybackType.Local) {
|
if (settings.type === PlaybackType.LOCAL) {
|
||||||
mpvPlayer.stop();
|
mpvPlayer.stop();
|
||||||
} else {
|
} else {
|
||||||
stopPlayback();
|
stopPlayback();
|
||||||
@@ -73,7 +73,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
const handleNextTrack = useCallback(() => {
|
const handleNextTrack = useCallback(() => {
|
||||||
const playerData = next();
|
const playerData = next();
|
||||||
|
|
||||||
if (settings.type === PlaybackType.Local) {
|
if (settings.type === PlaybackType.LOCAL) {
|
||||||
mpvPlayer.setQueue(playerData);
|
mpvPlayer.setQueue(playerData);
|
||||||
mpvPlayer.next();
|
mpvPlayer.next();
|
||||||
} else {
|
} else {
|
||||||
@@ -86,7 +86,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
const handlePrevTrack = useCallback(() => {
|
const handlePrevTrack = useCallback(() => {
|
||||||
const playerData = prev();
|
const playerData = prev();
|
||||||
|
|
||||||
if (settings.type === PlaybackType.Local) {
|
if (settings.type === PlaybackType.LOCAL) {
|
||||||
mpvPlayer.setQueue(playerData);
|
mpvPlayer.setQueue(playerData);
|
||||||
mpvPlayer.previous();
|
mpvPlayer.previous();
|
||||||
} else {
|
} else {
|
||||||
@@ -98,7 +98,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
|
|
||||||
const handlePlayPause = useCallback(() => {
|
const handlePlayPause = useCallback(() => {
|
||||||
if (queue) {
|
if (queue) {
|
||||||
if (playerStatus === PlayerStatus.Paused) {
|
if (playerStatus === PlayerStatus.PAUSED) {
|
||||||
return handlePlay();
|
return handlePlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
const handleSkipBackward = () => {
|
const handleSkipBackward = () => {
|
||||||
const skipBackwardSec = 5;
|
const skipBackwardSec = 5;
|
||||||
|
|
||||||
if (settings.type === PlaybackType.Local) {
|
if (settings.type === PlaybackType.LOCAL) {
|
||||||
const newTime = currentTime - skipBackwardSec;
|
const newTime = currentTime - skipBackwardSec;
|
||||||
mpvPlayer.seek(-skipBackwardSec);
|
mpvPlayer.seek(-skipBackwardSec);
|
||||||
setCurrentTime(newTime < 0 ? 0 : newTime);
|
setCurrentTime(newTime < 0 ? 0 : newTime);
|
||||||
@@ -126,7 +126,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
const handleSkipForward = () => {
|
const handleSkipForward = () => {
|
||||||
const skipForwardSec = 5;
|
const skipForwardSec = 5;
|
||||||
|
|
||||||
if (settings.type === PlaybackType.Local) {
|
if (settings.type === PlaybackType.LOCAL) {
|
||||||
const newTime = currentTime + skipForwardSec;
|
const newTime = currentTime + skipForwardSec;
|
||||||
mpvPlayer.seek(skipForwardSec);
|
mpvPlayer.seek(skipForwardSec);
|
||||||
setCurrentTime(newTime);
|
setCurrentTime(newTime);
|
||||||
@@ -147,7 +147,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
(e: number | any) => {
|
(e: number | any) => {
|
||||||
setCurrentTime(e);
|
setCurrentTime(e);
|
||||||
|
|
||||||
if (settings.type === PlaybackType.Local) {
|
if (settings.type === PlaybackType.LOCAL) {
|
||||||
mpvPlayer.seekTo(e);
|
mpvPlayer.seekTo(e);
|
||||||
} else {
|
} else {
|
||||||
currentPlayerRef.seekTo(e);
|
currentPlayerRef.seekTo(e);
|
||||||
+28
-26
@@ -1,23 +1,19 @@
|
|||||||
|
import { api } from '@/renderer/api';
|
||||||
import { Item, Play } from '../../../../types';
|
import { Item, Play } from '../../../../types';
|
||||||
import { albumsApi } from '../../../api/albumsApi';
|
import { useAuthStore, usePlayerStore } from '../../../store';
|
||||||
import { usePlayerStore } from '../../../store';
|
|
||||||
import {
|
|
||||||
getJellyfinStreamUrl,
|
|
||||||
getServerFolderAuth,
|
|
||||||
getSubsonicStreamUrl,
|
|
||||||
} from '../../../utils';
|
|
||||||
import { mpvPlayer } from '../utils/mpvPlayer';
|
import { mpvPlayer } from '../utils/mpvPlayer';
|
||||||
|
|
||||||
const getEndpointByItemType = (item: Item) => {
|
const getEndpointByItemType = (item: Item) => {
|
||||||
switch (item) {
|
switch (item) {
|
||||||
case Item.ALBUM:
|
case Item.ALBUM:
|
||||||
return albumsApi.getAlbum;
|
return api.albums.getAlbumDetail;
|
||||||
default:
|
default:
|
||||||
return albumsApi.getAlbum;
|
return api.albums.getAlbumDetail;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePlayQueueHandler = () => {
|
export const usePlayQueueHandler = () => {
|
||||||
|
const serverId = useAuthStore((state) => state.currentServer?.id) || '';
|
||||||
const addToQueue = usePlayerStore((state) => state.addToQueue);
|
const addToQueue = usePlayerStore((state) => state.addToQueue);
|
||||||
|
|
||||||
const handlePlayQueueAdd = async (options: {
|
const handlePlayQueueAdd = async (options: {
|
||||||
@@ -35,36 +31,42 @@ export const usePlayQueueHandler = () => {
|
|||||||
|
|
||||||
if (options.byItemType) {
|
if (options.byItemType) {
|
||||||
const deviceId = localStorage.getItem('device_id');
|
const deviceId = localStorage.getItem('device_id');
|
||||||
const { serverUrl } = JSON.parse(
|
// const { state } = JSON.parse(
|
||||||
localStorage.getItem('authentication') || '{}'
|
// localStorage.getItem('authentication') || '{}'
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (deviceId) {
|
if (deviceId) {
|
||||||
const endpoint = getEndpointByItemType(options.byItemType.type);
|
const endpoint = getEndpointByItemType(options.byItemType.type);
|
||||||
|
|
||||||
const { data } = await endpoint({
|
const { data } = await endpoint({
|
||||||
id: options.byItemType.id,
|
albumId: options.byItemType.id,
|
||||||
|
serverId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const songs = data.songs.map((song) => {
|
const songs = data.songs?.map((song) => {
|
||||||
const auth = getServerFolderAuth(serverUrl, song.serverFolderId);
|
// const auth = getServerFolderAuth(
|
||||||
|
// state.serverUrl,
|
||||||
|
// song.serverFolderId
|
||||||
|
// );
|
||||||
|
|
||||||
if (auth) {
|
// if (auth) {
|
||||||
const streamUrl =
|
// const streamUrl =
|
||||||
auth.type === 'jellyfin'
|
// auth.type === 'jellyfin'
|
||||||
? getJellyfinStreamUrl(auth, song, deviceId)
|
// ? getJellyfinStreamUrl(auth, song, deviceId)
|
||||||
: getSubsonicStreamUrl(auth, song, deviceId);
|
// : getSubsonicStreamUrl(auth, song, deviceId);
|
||||||
|
|
||||||
return {
|
// return {
|
||||||
...song,
|
// ...song,
|
||||||
streamUrl,
|
// streamUrl,
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
return song;
|
return song;
|
||||||
});
|
});
|
||||||
|
|
||||||
const playerData = addToQueue(songs, options.play);
|
const playerData = addToQueue(songs || [], options.play);
|
||||||
|
|
||||||
|
console.log('playerData', playerData);
|
||||||
|
|
||||||
if (options.play === Play.NEXT || options.play === Play.LAST) {
|
if (options.play === Play.NEXT || options.play === Play.LAST) {
|
||||||
mpvPlayer.setQueueNext(playerData);
|
mpvPlayer.setQueueNext(playerData);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export * from './components/CenterControls';
|
export * from './components/center-controls';
|
||||||
export * from './components/LeftControls';
|
export * from './components/left-controls';
|
||||||
export * from './components/Playerbar';
|
export * from './components/playerbar';
|
||||||
export * from './components/Slider';
|
export * from './components/slider';
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
interface AnimatedPageProps {
|
interface AnimatedPageProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useAuthStore } from '@/renderer/store';
|
||||||
|
|
||||||
|
export const usePermissions = () => {
|
||||||
|
const permissions = useAuthStore((state) => state.permissions);
|
||||||
|
|
||||||
|
const permissionSet = useMemo(() => {
|
||||||
|
const set = {
|
||||||
|
createServer: permissions.isAdmin,
|
||||||
|
createServerCredential: true,
|
||||||
|
createServerUrl: permissions.isAdmin,
|
||||||
|
deleteServer: permissions.isAdmin,
|
||||||
|
deleteServerCredential: true,
|
||||||
|
deleteServerUrl: permissions.isAdmin,
|
||||||
|
editServer: permissions.isAdmin,
|
||||||
|
};
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}, [permissions]);
|
||||||
|
|
||||||
|
return permissionSet;
|
||||||
|
};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './components/animated-page';
|
||||||
|
export * from './hooks/use-permissions';
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
import {
|
import {
|
||||||
RiDashboardFill,
|
RiDashboardFill,
|
||||||
RiFileList2Fill,
|
RiFileList2Fill,
|
||||||
RiSearch2Fill,
|
RiSearch2Line,
|
||||||
} from 'react-icons/ri';
|
} from 'react-icons/ri';
|
||||||
import styled from 'styled-components';
|
import { AppRoute } from '../../../router/routes';
|
||||||
import { AppRoute } from '../../../router/utils/routes';
|
import { ListItem } from './list-item';
|
||||||
import { ListItem } from './ListItem';
|
|
||||||
|
|
||||||
const StyledSidebar = styled.div``;
|
const StyledSidebar = styled.div``;
|
||||||
|
|
||||||
@@ -19,15 +19,15 @@ export const Sidebar = () => {
|
|||||||
</ListItem.Link>
|
</ListItem.Link>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItem.Link to={AppRoute.SEARCH}>
|
<ListItem.Link to={AppRoute.LIBRARY}>
|
||||||
<RiSearch2Fill size={20} />
|
<RiFileList2Fill size={20} />
|
||||||
Search
|
Library
|
||||||
</ListItem.Link>
|
</ListItem.Link>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItem.Link to={AppRoute.LIBRARY}>
|
<ListItem.Link to={AppRoute.SEARCH}>
|
||||||
<RiFileList2Fill size={20} />
|
<RiSearch2Line size={20} />
|
||||||
Your Library
|
Search
|
||||||
</ListItem.Link>
|
</ListItem.Link>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</StyledSidebar>
|
</StyledSidebar>
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
import { css } from '@emotion/react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Link, LinkProps } from 'react-router-dom';
|
import { Link, LinkProps } from 'react-router-dom';
|
||||||
import styled, { css } from 'styled-components';
|
|
||||||
import { fontInter } from '../../../styles';
|
import { fontInter } from '../../../styles';
|
||||||
|
|
||||||
interface ListItemProps {
|
interface ListItemProps {
|
||||||
@@ -1 +1 @@
|
|||||||
export * from './components/Sidebar';
|
export * from './components/sidebar';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import { QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { I18nextProvider } from 'react-i18next';
|
import { I18nextProvider } from 'react-i18next';
|
||||||
import { QueryClientProvider } from 'react-query';
|
import { queryClient } from '@/renderer/lib/react-query';
|
||||||
import i18n from '../i18n/i18n';
|
import i18n from '../i18n/i18n';
|
||||||
import { App } from './app';
|
import { App } from './app';
|
||||||
import { queryClient } from './lib';
|
|
||||||
|
|
||||||
const container = document.getElementById('root')!;
|
const container = document.getElementById('root')!;
|
||||||
const root = createRoot(container);
|
const root = createRoot(container);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AppRoute } from './router/utils/routes';
|
import { AppRoute } from './router/routes';
|
||||||
|
|
||||||
export interface CardRow {
|
export interface CardRow {
|
||||||
align?: 'left' | 'center' | 'right';
|
align?: 'left' | 'center' | 'right';
|
||||||
|
|||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import '@emotion/react';
|
||||||
|
import type { MantineTheme } from '@mantine/core';
|
||||||
|
|
||||||
|
declare module '@emotion/react' {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface GREY extends MantineTheme {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user