fix all imports for new structure

This commit is contained in:
jeffvli 2025-05-20 19:23:36 -07:00
parent 249eaf89f8
commit 930165d006
291 changed files with 2056 additions and 1894 deletions

View file

@ -1,8 +1,10 @@
import { useEffect } from 'react';
import { MantineProvider } from '@mantine/core';
import { useEffect } from 'react';
import './styles/global.scss';
import { useIsDark, useReconnect } from '/@/remote/store';
import { Shell } from '/@/remote/components/shell';
import { useIsDark, useReconnect } from '/@/remote/store';
export const App = () => {
const isDark = useIsDark();
@ -14,8 +16,6 @@ export const App = () => {
return (
<MantineProvider
withGlobalStyles
withNormalizeCSS
theme={{
colorScheme: isDark ? 'dark' : 'light',
components: {
@ -77,6 +77,8 @@ export const App = () => {
xs: '0rem',
},
}}
withGlobalStyles
withNormalizeCSS
>
<Shell />
</MantineProvider>

View file

@ -1,4 +1,5 @@
import { CiImageOff, CiImageOn } from 'react-icons/ci';
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
import { useShowImage, useToggleShowImage } from '/@/remote/store';
@ -9,10 +10,10 @@ export const ImageButton = () => {
return (
<RemoteButton
mr={5}
onClick={() => toggleImage()}
size="xl"
tooltip={showImage ? 'Hide Image' : 'Show Image'}
variant="default"
onClick={() => toggleImage()}
>
{showImage ? <CiImageOff size={30} /> : <CiImageOn size={30} />}
</RemoteButton>

View file

@ -1,6 +1,7 @@
import { RiRestartLine } from 'react-icons/ri';
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
import { useConnected, useReconnect } from '/@/remote/store';
import { RiRestartLine } from 'react-icons/ri';
export const ReconnectButton = () => {
const connected = useConnected();
@ -10,10 +11,10 @@ export const ReconnectButton = () => {
<RemoteButton
$active={!connected}
mr={5}
onClick={() => reconnect()}
size="xl"
tooltip={connected ? 'Reconnect' : 'Not connected. Reconnect.'}
variant="default"
onClick={() => reconnect()}
>
<RiRestartLine size={30} />
</RemoteButton>

View file

@ -1,8 +1,11 @@
import { MouseEvent, ReactNode, Ref, forwardRef } from 'react';
import { Button, type ButtonProps as MantineButtonProps } from '@mantine/core';
import { Tooltip } from '/@/renderer/components/tooltip';
import { Button, type ButtonProps as MantineButtonProps, Tooltip } from '@mantine/core';
import { forwardRef, MouseEvent, ReactNode, Ref } from 'react';
import styled from 'styled-components';
export interface ButtonProps extends StyledButtonProps {
tooltip: string;
}
interface StyledButtonProps extends MantineButtonProps {
$active?: boolean;
children: ReactNode;
@ -11,10 +14,6 @@ interface StyledButtonProps extends MantineButtonProps {
ref: Ref<HTMLButtonElement>;
}
export interface ButtonProps extends StyledButtonProps {
tooltip: string;
}
const StyledButton = styled(Button)<StyledButtonProps>`
svg {
display: flex;
@ -35,12 +34,12 @@ const StyledButton = styled(Button)<StyledButtonProps>`
}
`;
export const RemoteButton = forwardRef<HTMLButtonElement, ButtonProps>(
({ children, tooltip, ...props }: ButtonProps, ref) => {
export const RemoteButton = forwardRef<HTMLButtonElement, any>(
({ children, tooltip, ...props }: any, ref) => {
return (
<Tooltip
withinPortal
label={tooltip}
withinPortal
>
<StyledButton
{...props}
@ -52,9 +51,3 @@ export const RemoteButton = forwardRef<HTMLButtonElement, ButtonProps>(
);
},
);
RemoteButton.defaultProps = {
$active: false,
onClick: undefined,
onMouseDown: undefined,
};

View file

@ -1,8 +1,9 @@
import { useIsDark, useToggleDark } from '/@/remote/store';
import { RiMoonLine, RiSunLine } from 'react-icons/ri';
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
import { AppTheme } from '/@/renderer/themes/types';
import { useEffect } from 'react';
import { RiMoonLine, RiSunLine } from 'react-icons/ri';
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
import { useIsDark, useToggleDark } from '/@/remote/store';
import { AppTheme } from '/@/shared/types/domain-types';
export const ThemeButton = () => {
const isDark = useIsDark();
@ -16,10 +17,10 @@ export const ThemeButton = () => {
return (
<RemoteButton
mr={5}
onClick={() => toggleDark()}
size="xl"
tooltip="Toggle Theme"
variant="default"
onClick={() => toggleDark()}
>
{isDark ? <RiSunLine size={30} /> : <RiMoonLine size={30} />}
</RemoteButton>

View file

@ -1,9 +1,7 @@
import { useCallback } from 'react';
import { Group, Image, Text, Title } from '@mantine/core';
import { useInfo, useSend, useShowImage } from '/@/remote/store';
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
import { Group, Image, Rating, Text, Title, Tooltip } from '@mantine/core';
import formatDuration from 'format-duration';
import debounce from 'lodash/debounce';
import { useCallback } from 'react';
import {
RiHeartLine,
RiPauseFill,
@ -15,10 +13,11 @@ import {
RiSkipForwardFill,
RiVolumeUpFill,
} from 'react-icons/ri';
import { PlayerRepeat, PlayerStatus } from '/@/renderer/types';
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
import { WrapperSlider } from '/@/remote/components/wrapped-slider';
import { Tooltip } from '/@/renderer/components/tooltip';
import { Rating } from '/@/renderer/components/rating';
import { useInfo, useSend, useShowImage } from '/@/remote/store';
import { PlayerRepeat, PlayerStatus } from '/@/shared/types/types';
export const RemoteContainer = () => {
const { position, repeat, shuffle, song, status, volume } = useInfo();
@ -62,16 +61,14 @@ export const RemoteContainer = () => {
>
<RemoteButton
disabled={!id}
onClick={() => send({ event: 'previous' })}
tooltip="Previous track"
variant="default"
onClick={() => send({ event: 'previous' })}
>
<RiSkipBackFill size={25} />
</RemoteButton>
<RemoteButton
disabled={!id}
tooltip={id && status === PlayerStatus.PLAYING ? 'Pause' : 'Play'}
variant="default"
onClick={() => {
if (status === PlayerStatus.PLAYING) {
send({ event: 'pause' });
@ -79,6 +76,8 @@ export const RemoteContainer = () => {
send({ event: 'play' });
}
}}
tooltip={id && status === PlayerStatus.PLAYING ? 'Pause' : 'Play'}
variant="default"
>
{id && status === PlayerStatus.PLAYING ? (
<RiPauseFill size={25} />
@ -88,9 +87,9 @@ export const RemoteContainer = () => {
</RemoteButton>
<RemoteButton
disabled={!id}
onClick={() => send({ event: 'next' })}
tooltip="Next track"
variant="default"
onClick={() => send({ event: 'next' })}
>
<RiSkipForwardFill size={25} />
</RemoteButton>
@ -101,14 +100,15 @@ export const RemoteContainer = () => {
>
<RemoteButton
$active={shuffle || false}
onClick={() => send({ event: 'shuffle' })}
tooltip={shuffle ? 'Shuffle tracks' : 'Shuffle disabled'}
variant="default"
onClick={() => send({ event: 'shuffle' })}
>
<RiShuffleFill size={25} />
</RemoteButton>
<RemoteButton
$active={repeat !== undefined && repeat !== PlayerRepeat.NONE}
onClick={() => send({ event: 'repeat' })}
tooltip={`Repeat ${
repeat === PlayerRepeat.ONE
? 'One'
@ -117,7 +117,6 @@ export const RemoteContainer = () => {
: 'none'
}`}
variant="default"
onClick={() => send({ event: 'repeat' })}
>
{repeat === undefined || repeat === PlayerRepeat.ONE ? (
<RiRepeatOneLine size={25} />
@ -128,13 +127,13 @@ export const RemoteContainer = () => {
<RemoteButton
$active={song?.userFavorite}
disabled={!id}
tooltip={song?.userFavorite ? 'Unfavorite' : 'Favorite'}
variant="default"
onClick={() => {
if (!id) return;
send({ event: 'favorite', favorite: !song.userFavorite, id });
}}
tooltip={song?.userFavorite ? 'Unfavorite' : 'Favorite'}
variant="default"
>
<RiHeartLine size={25} />
</RemoteButton>
@ -145,10 +144,10 @@ export const RemoteContainer = () => {
openDelay={1000}
>
<Rating
sx={{ margin: 'auto' }}
value={song.userRating ?? 0}
onChange={debouncedSetRating}
onDoubleClick={() => debouncedSetRating(0)}
sx={{ margin: 'auto' }}
value={song.userRating ?? 0}
/>
</Tooltip>
</div>
@ -159,14 +158,15 @@ export const RemoteContainer = () => {
label={(value) => formatDuration(value * 1e3)}
leftLabel={formatDuration(position * 1e3)}
max={song.duration / 1e3}
onChangeEnd={(e) => send({ event: 'position', position: e })}
rightLabel={formatDuration(song.duration)}
value={position}
onChangeEnd={(e) => send({ event: 'position', position: e })}
/>
)}
<WrapperSlider
leftLabel={<RiVolumeUpFill size={20} />}
max={100}
onChangeEnd={(e) => send({ event: 'volume', volume: e })}
rightLabel={
<Text
size="xs"
@ -176,12 +176,11 @@ export const RemoteContainer = () => {
</Text>
}
value={volume ?? 0}
onChangeEnd={(e) => send({ event: 'volume', volume: e })}
/>
{showImage && (
<Image
src={song?.imageUrl?.replaceAll(/&(size|width|height=\d+)/g, '')}
onError={() => send({ event: 'proxy' })}
src={song?.imageUrl?.replaceAll(/&(size|width|height=\d+)/g, '')}
/>
)}
</>

View file

@ -9,10 +9,11 @@ import {
Skeleton,
Title,
} from '@mantine/core';
import { ThemeButton } from '/@/remote/components/buttons/theme-button';
import { ImageButton } from '/@/remote/components/buttons/image-button';
import { RemoteContainer } from '/@/remote/components/remote-container';
import { ReconnectButton } from '/@/remote/components/buttons/reconnect-button';
import { ThemeButton } from '/@/remote/components/buttons/theme-button';
import { RemoteContainer } from '/@/remote/components/remote-container';
import { useConnected } from '/@/remote/store';
export const Shell = () => {

View file

@ -1,7 +1,6 @@
import { useState, ReactNode } from 'react';
import { SliderProps } from '@mantine/core';
import { rem, Slider, SliderProps } from '@mantine/core';
import { ReactNode, useState } from 'react';
import styled from 'styled-components';
import { PlayerbarSlider } from '/@/renderer/features/player/components/playerbar-slider';
const SliderContainer = styled.div`
display: flex;
@ -25,6 +24,54 @@ const SliderWrapper = styled.div`
height: 100%;
`;
const PlayerbarSlider = ({ ...props }: SliderProps) => {
return (
<Slider
styles={{
bar: {
backgroundColor: 'var(--playerbar-slider-track-progress-bg)',
transition: 'background-color 0.2s ease',
},
label: {
backgroundColor: 'var(--tooltip-bg)',
color: 'var(--tooltip-fg)',
fontSize: '1.1rem',
fontWeight: 600,
padding: '0 1rem',
},
root: {
'&:hover': {
'& .mantine-Slider-bar': {
backgroundColor: 'var(--primary-color)',
},
'& .mantine-Slider-thumb': {
opacity: 1,
},
},
},
thumb: {
backgroundColor: 'var(--slider-thumb-bg)',
borderColor: 'var(--primary-color)',
borderWidth: rem(1),
height: '1rem',
opacity: 0,
width: '1rem',
},
track: {
'&::before': {
backgroundColor: 'var(--playerbar-slider-track-bg)',
right: 'calc(0.1rem * -1)',
},
},
}}
{...props}
onClick={(e) => {
e?.stopPropagation();
}}
/>
);
};
export interface WrappedProps extends Omit<SliderProps, 'onChangeEnd'> {
leftLabel?: ReactNode;
onChangeEnd: (value: number) => void;
@ -43,9 +90,6 @@ export const WrapperSlider = ({ leftLabel, rightLabel, value, ...props }: Wrappe
<PlayerbarSlider
{...props}
min={0}
size={6}
value={!isSeeking ? (value ?? 0) : seek}
w="100%"
onChange={(e) => {
setIsSeeking(true);
setSeek(e);
@ -54,6 +98,9 @@ export const WrapperSlider = ({ leftLabel, rightLabel, value, ...props }: Wrappe
props.onChangeEnd(e);
setIsSeeking(false);
}}
size={6}
value={!isSeeking ? (value ?? 0) : seek}
w="100%"
/>
</SliderWrapper>
{rightLabel && <SliderValueWrapper $position="right">{rightLabel}</SliderValueWrapper>}

View file

@ -1,5 +1,6 @@
import { Notifications } from '@mantine/notifications';
import { createRoot } from 'react-dom/client';
import { App } from '/@/remote/app';
const container = document.getElementById('root')! as HTMLElement;

View file

@ -1,10 +1,9 @@
/// <reference lib="WebWorker" />
export type {};
// eslint-disable-next-line no-undef
declare const self: ServiceWorkerGlobalScope;
// eslint-disable-next-line no-restricted-globals
const url = new URL(location.toString());
const version = url.searchParams.get('version');
const prod = url.searchParams.get('prod') === 'true';

View file

@ -1,13 +1,20 @@
import { hideNotification, showNotification } from '@mantine/notifications';
import type { NotificationProps as MantineNotificationProps } from '@mantine/notifications';
import { hideNotification, showNotification } from '@mantine/notifications';
import merge from 'lodash/merge';
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import type { ClientEvent, ServerEvent, SongUpdateSocket } from '/@/remote/types';
interface StatefulWebSocket extends WebSocket {
natural: boolean;
import { ClientEvent, ServerEvent, SongUpdateSocket } from '/@/shared/types/remote-types';
export interface SettingsSlice extends SettingsState {
actions: {
reconnect: () => void;
send: (data: ClientEvent) => void;
toggleIsDark: () => void;
toggleShowImage: () => void;
};
}
interface SettingsState {
@ -18,13 +25,8 @@ interface SettingsState {
socket?: StatefulWebSocket;
}
export interface SettingsSlice extends SettingsState {
actions: {
reconnect: () => void;
send: (data: ClientEvent) => void;
toggleIsDark: () => void;
toggleShowImage: () => void;
};
interface StatefulWebSocket extends WebSocket {
natural: boolean;
}
const initialState: SettingsState = {
@ -106,19 +108,18 @@ export const useRemoteStore = create<SettingsSlice>()(
const credentials = await fetch('/credentials');
authHeader = await credentials.text();
} catch (error) {
console.error('Failed to get credentials');
console.error('Failed to get credentials', error);
}
set((state) => {
const socket = new WebSocket(
// eslint-disable-next-line no-restricted-globals
location.href.replace('http', 'ws'),
) as StatefulWebSocket;
socket.natural = false;
socket.addEventListener('message', (message) => {
const { event, data } = JSON.parse(message.data) as ServerEvent;
const { data, event } = JSON.parse(message.data) as ServerEvent;
switch (event) {
case 'error': {
@ -207,7 +208,6 @@ export const useRemoteStore = create<SettingsSlice>()(
socket.addEventListener('close', (reason) => {
if (reason.code === 4002 || reason.code === 4003) {
// eslint-disable-next-line no-restricted-globals
location.reload();
} else if (reason.code === 4000) {
toast.warn({

View file

@ -1,113 +0,0 @@
import type { QueueSong } from '/@/renderer/api/types';
import type { PlayerRepeat, PlayerStatus, SongState } from '/@/renderer/types';
export interface SongUpdateSocket extends Omit<SongState, 'song'> {
position?: number;
song?: QueueSong | null;
}
export interface ServerError {
data: string;
event: 'error';
}
export interface ServerFavorite {
data: { favorite: boolean; id: string };
event: 'favorite';
}
export interface ServerPlayStatus {
data: PlayerStatus;
event: 'playback';
}
export interface ServerPosition {
data: number;
event: 'position';
}
export interface ServerProxy {
data: string;
event: 'proxy';
}
export interface ServerRating {
data: { id: string; rating: number };
event: 'rating';
}
export interface ServerRepeat {
data: PlayerRepeat;
event: 'repeat';
}
export interface ServerShuffle {
data: boolean;
event: 'shuffle';
}
export interface ServerSong {
data: QueueSong | null;
event: 'song';
}
export interface ServerState {
data: SongState;
event: 'state';
}
export interface ServerVolume {
data: number;
event: 'volume';
}
export type ServerEvent =
| ServerError
| ServerFavorite
| ServerPlayStatus
| ServerPosition
| ServerRating
| ServerRepeat
| ServerShuffle
| ServerSong
| ServerState
| ServerProxy
| ServerVolume;
export interface ClientSimpleEvent {
event: 'next' | 'pause' | 'play' | 'previous' | 'proxy' | 'repeat' | 'shuffle';
}
export interface ClientFavorite {
event: 'favorite';
favorite: boolean;
id: string;
}
export interface ClientRating {
event: 'rating';
id: string;
rating: number;
}
export interface ClientVolume {
event: 'volume';
volume: number;
}
export interface ClientAuth {
event: 'authenticate';
header: string;
}
export interface ClientPosition {
event: 'position';
position: number;
}
export type ClientEvent =
| ClientAuth
| ClientPosition
| ClientSimpleEvent
| ClientFavorite
| ClientRating
| ClientVolume;