mirror of
https://github.com/jeffvli/feishin.git
synced 2024-11-20 14:37:06 +01:00
[feat]: Remote Improvements
- Make the interface a bit more responsive (menu, button changes) - Enable disabling screen lock (needs better icon) - Improve updated rating
This commit is contained in:
parent
adc09e6bbf
commit
a784fbd060
45
package-lock.json
generated
45
package-lock.json
generated
@ -5,7 +5,6 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "feishin",
|
||||
"version": "0.5.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0",
|
||||
@ -52,6 +51,7 @@
|
||||
"nanoid": "^3.3.3",
|
||||
"net": "^1.0.2",
|
||||
"node-mpv": "github:jeffvli/Node-MPV",
|
||||
"nosleep.js": "^0.12.0",
|
||||
"overlayscrollbars": "^2.2.1",
|
||||
"overlayscrollbars-react": "^0.5.1",
|
||||
"react": "^18.2.0",
|
||||
@ -219,6 +219,8 @@
|
||||
"integrity": "sha512-prtg5f6zCERIaECeTZzd2fMtVjlfjhUcO+fBLQ6DXXdq5FljN+excVitJ2nogsusdf31LeqkjAfXZ7Xq+HmN8g==",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
|
||||
"chokidar": "^3.4.0",
|
||||
"commander": "^4.0.1",
|
||||
"convert-source-map": "^1.1.0",
|
||||
"fs-readdir-recursive": "^1.1.0",
|
||||
@ -2384,6 +2386,7 @@
|
||||
"debug": "^4.1.1",
|
||||
"env-paths": "^2.2.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"global-agent": "^3.0.0",
|
||||
"got": "^11.8.5",
|
||||
"progress": "^2.0.3",
|
||||
"semver": "^6.2.0",
|
||||
@ -2439,6 +2442,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -2507,6 +2511,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -2698,6 +2703,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -2758,6 +2764,7 @@
|
||||
"integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"encoding": "^0.1.13",
|
||||
"minipass": "^3.1.6",
|
||||
"minipass-sized": "^1.0.3",
|
||||
"minizlib": "^2.1.2"
|
||||
@ -2945,6 +2952,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -3729,6 +3737,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -4504,6 +4513,7 @@
|
||||
"dependencies": {
|
||||
"camelcase": "^5.3.1",
|
||||
"loader-utils": "^1.4.2",
|
||||
"prettier": "*",
|
||||
"schema-utils": "^2.0.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -6031,6 +6041,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -6943,6 +6954,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -7199,6 +7211,7 @@
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"fsevents": "~2.3.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
@ -8637,6 +8650,7 @@
|
||||
"app-builder-lib": "24.6.3",
|
||||
"builder-util": "24.5.0",
|
||||
"builder-util-runtime": "9.2.1",
|
||||
"dmg-license": "^1.0.11",
|
||||
"fs-extra": "^10.1.0",
|
||||
"iconv-lite": "^0.6.2",
|
||||
"js-yaml": "^4.1.0"
|
||||
@ -8678,6 +8692,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -9010,6 +9025,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -9137,6 +9153,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -9200,6 +9217,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -9276,6 +9294,7 @@
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -9581,7 +9600,8 @@
|
||||
"esprima": "^4.0.1",
|
||||
"estraverse": "^5.2.0",
|
||||
"esutils": "^2.0.2",
|
||||
"optionator": "^0.8.1"
|
||||
"optionator": "^0.8.1",
|
||||
"source-map": "~0.6.1"
|
||||
},
|
||||
"bin": {
|
||||
"escodegen": "bin/escodegen.js",
|
||||
@ -10752,6 +10772,7 @@
|
||||
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/yauzl": "^2.9.1",
|
||||
"debug": "^4.1.1",
|
||||
"get-stream": "^5.1.0",
|
||||
"yauzl": "^2.10.0"
|
||||
@ -11106,6 +11127,7 @@
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.13.0.tgz",
|
||||
"integrity": "sha512-xKhw9VCizmwEHbopOfluaoVunGHSZyMztGbTvsgOYqCjaKu6qtlwWY1J+6GhL41NY1P157JgEikjDm67XCFnvQ==",
|
||||
"dependencies": {
|
||||
"@emotion/is-prop-valid": "^0.8.2",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -12247,6 +12269,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -13396,6 +13419,7 @@
|
||||
"@types/node": "*",
|
||||
"anymatch": "^3.0.3",
|
||||
"fb-watchman": "^2.0.0",
|
||||
"fsevents": "^2.3.2",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-regex-util": "^27.5.1",
|
||||
"jest-serializer": "^27.5.1",
|
||||
@ -13942,6 +13966,9 @@
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
@ -15229,6 +15256,11 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/nosleep.js": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/nosleep.js/-/nosleep.js-0.12.0.tgz",
|
||||
"integrity": "sha512-9d1HbpKLh3sdWlhXMhU6MMH+wQzKkrgfRkYV0EBdvt99YJfj0ilCJrWRDYG2130Tm4GXbEoTCx5b34JSaP+HhA=="
|
||||
},
|
||||
"node_modules/now-and-later": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz",
|
||||
@ -18875,6 +18907,9 @@
|
||||
"resolved": "https://registry.npmjs.org/stylelint-config-css-modules/-/stylelint-config-css-modules-4.3.0.tgz",
|
||||
"integrity": "sha512-KvIvhzzjpcjHKkGSPkQCueoZJHrb6sZ6GCtrQb/J45HQTBVwJAeNYXaSZZK6ZQOC7NxJ4v5kLxpQLDiCK6zzgw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"stylelint-scss": "^5.0.0 || ^6.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"stylelint-scss": "^5.0.0 || ^6.0.0"
|
||||
},
|
||||
@ -19432,6 +19467,7 @@
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -32716,6 +32752,11 @@
|
||||
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
|
||||
"dev": true
|
||||
},
|
||||
"nosleep.js": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/nosleep.js/-/nosleep.js-0.12.0.tgz",
|
||||
"integrity": "sha512-9d1HbpKLh3sdWlhXMhU6MMH+wQzKkrgfRkYV0EBdvt99YJfj0ilCJrWRDYG2130Tm4GXbEoTCx5b34JSaP+HhA=="
|
||||
},
|
||||
"now-and-later": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz",
|
||||
|
@ -331,6 +331,7 @@
|
||||
"nanoid": "^3.3.3",
|
||||
"net": "^1.0.2",
|
||||
"node-mpv": "github:jeffvli/Node-MPV",
|
||||
"nosleep.js": "^0.12.0",
|
||||
"overlayscrollbars": "^2.2.1",
|
||||
"overlayscrollbars-react": "^0.5.1",
|
||||
"react": "^18.2.0",
|
||||
|
@ -3,6 +3,7 @@ import { MantineProvider } from '@mantine/core';
|
||||
import './styles/global.scss';
|
||||
import { useIsDark, useReconnect } from '/@/remote/store';
|
||||
import { Shell } from '/@/remote/components/shell';
|
||||
import { AppTheme } from '/@/renderer/themes/types';
|
||||
|
||||
export const App = () => {
|
||||
const isDark = useIsDark();
|
||||
@ -12,6 +13,11 @@ export const App = () => {
|
||||
reconnect();
|
||||
}, [reconnect]);
|
||||
|
||||
useEffect(() => {
|
||||
const targetTheme: AppTheme = isDark ? AppTheme.DEFAULT_DARK : AppTheme.DEFAULT_LIGHT;
|
||||
document.body.setAttribute('data-theme', targetTheme);
|
||||
}, [isDark]);
|
||||
|
||||
return (
|
||||
<MantineProvider
|
||||
withGlobalStyles
|
||||
|
@ -8,10 +8,7 @@ export const ImageButton = () => {
|
||||
|
||||
return (
|
||||
<RemoteButton
|
||||
mr={5}
|
||||
size="xl"
|
||||
tooltip={showImage ? 'Hide Image' : 'Show Image'}
|
||||
variant="default"
|
||||
onClick={() => toggleImage()}
|
||||
>
|
||||
{showImage ? <CiImageOff size={30} /> : <CiImageOn size={30} />}
|
||||
|
@ -9,10 +9,7 @@ export const ReconnectButton = () => {
|
||||
return (
|
||||
<RemoteButton
|
||||
$active={!connected}
|
||||
mr={5}
|
||||
size="xl"
|
||||
tooltip={connected ? 'Reconnect' : 'Not connected. Reconnect.'}
|
||||
variant="default"
|
||||
onClick={() => reconnect()}
|
||||
>
|
||||
<RiRestartLine size={30} />
|
||||
|
@ -12,7 +12,7 @@ interface StyledButtonProps extends MantineButtonProps {
|
||||
}
|
||||
|
||||
export interface ButtonProps extends StyledButtonProps {
|
||||
tooltip: string;
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
const StyledButton = styled(Button)<StyledButtonProps>`
|
||||
@ -37,19 +37,29 @@ const StyledButton = styled(Button)<StyledButtonProps>`
|
||||
|
||||
export const RemoteButton = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ children, tooltip, ...props }: ButtonProps, ref) => {
|
||||
return (
|
||||
<Tooltip
|
||||
withinPortal
|
||||
label={tooltip}
|
||||
>
|
||||
const button = (
|
||||
<StyledButton
|
||||
fullWidth
|
||||
size="xl"
|
||||
variant="default"
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</StyledButton>
|
||||
);
|
||||
if (tooltip) {
|
||||
return (
|
||||
<Tooltip
|
||||
withinPortal
|
||||
events={{ focus: true, hover: true, touch: true }}
|
||||
label={tooltip}
|
||||
>
|
||||
{button}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return button;
|
||||
},
|
||||
);
|
||||
|
||||
|
18
src/remote/components/buttons/sleep-button.tsx
Normal file
18
src/remote/components/buttons/sleep-button.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
|
||||
import { LuMonitor, LuMonitorOff } from 'react-icons/lu';
|
||||
import { useNoSleepContext } from '/@/remote/context/nosleep-context';
|
||||
import { useToggleNoSleep } from '/@/remote/hooks/use-toggle-no-sleep';
|
||||
|
||||
export const SleepButton = () => {
|
||||
const { enabled } = useNoSleepContext();
|
||||
const toggleNoSleep = useToggleNoSleep();
|
||||
|
||||
return (
|
||||
<RemoteButton
|
||||
tooltip={enabled ? 'Enable screen lock' : 'Disable screen lock'}
|
||||
onClick={toggleNoSleep}
|
||||
>
|
||||
{enabled ? <LuMonitorOff size={30} /> : <LuMonitor size={30} />}
|
||||
</RemoteButton>
|
||||
);
|
||||
};
|
@ -1,24 +1,14 @@
|
||||
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';
|
||||
|
||||
export const ThemeButton = () => {
|
||||
const isDark = useIsDark();
|
||||
const toggleDark = useToggleDark();
|
||||
|
||||
useEffect(() => {
|
||||
const targetTheme: AppTheme = isDark ? AppTheme.DEFAULT_DARK : AppTheme.DEFAULT_LIGHT;
|
||||
document.body.setAttribute('data-theme', targetTheme);
|
||||
}, [isDark]);
|
||||
|
||||
return (
|
||||
<RemoteButton
|
||||
mr={5}
|
||||
size="xl"
|
||||
tooltip="Toggle Theme"
|
||||
variant="default"
|
||||
onClick={() => toggleDark()}
|
||||
>
|
||||
{isDark ? <RiSunLine size={30} /> : <RiMoonLine size={30} />}
|
||||
|
49
src/remote/components/menu.tsx
Normal file
49
src/remote/components/menu.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { CiImageOff, CiImageOn } from 'react-icons/ci';
|
||||
import { LuMonitor, LuMonitorOff } from 'react-icons/lu';
|
||||
import { RiMenuFill, RiMoonLine, RiSunLine } from 'react-icons/ri';
|
||||
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
|
||||
import { DropdownMenu } from '/@/renderer/components/dropdown-menu';
|
||||
import { useIsDark, useShowImage, useToggleDark, useToggleShowImage } from '/@/remote/store';
|
||||
import { useNoSleepContext } from '/@/remote/context/nosleep-context';
|
||||
import { useToggleNoSleep } from '/@/remote/hooks/use-toggle-no-sleep';
|
||||
|
||||
export const ResponsiveMenu = () => {
|
||||
const showImage = useShowImage();
|
||||
const toggleImage = useToggleShowImage();
|
||||
const isDark = useIsDark();
|
||||
const toggleDark = useToggleDark();
|
||||
|
||||
const { enabled } = useNoSleepContext();
|
||||
const toggleNoSleep = useToggleNoSleep();
|
||||
|
||||
return (
|
||||
<DropdownMenu closeOnItemClick={false}>
|
||||
<DropdownMenu.Target>
|
||||
<RemoteButton>
|
||||
<RiMenuFill size={30} />
|
||||
</RemoteButton>
|
||||
</DropdownMenu.Target>
|
||||
|
||||
<DropdownMenu.Dropdown>
|
||||
<DropdownMenu.Item
|
||||
icon={showImage ? <CiImageOff size={30} /> : <CiImageOn size={30} />}
|
||||
onClick={toggleImage}
|
||||
>
|
||||
{showImage ? 'Hide Image' : 'Show Image'}
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
icon={isDark ? <RiSunLine size={30} /> : <RiMoonLine size={30} />}
|
||||
onClick={toggleDark}
|
||||
>
|
||||
Toggle Theme
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
icon={enabled ? <LuMonitorOff size={30} /> : <LuMonitor size={30} />}
|
||||
onClick={toggleNoSleep}
|
||||
>
|
||||
{enabled ? 'Enable screen lock' : 'Disable screen lock'}
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
@ -1,9 +1,8 @@
|
||||
import { useCallback } from 'react';
|
||||
import { Group, Image, Text, Title } from '@mantine/core';
|
||||
import { Center, Grid, Group, Image, MediaQuery, Text, Title } from '@mantine/core';
|
||||
import { useInfo, useSend, useShowImage } from '/@/remote/store';
|
||||
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
|
||||
import formatDuration from 'format-duration';
|
||||
import debounce from 'lodash/debounce';
|
||||
import {
|
||||
RiHeartLine,
|
||||
RiPauseFill,
|
||||
@ -17,7 +16,6 @@ import {
|
||||
} from 'react-icons/ri';
|
||||
import { PlayerRepeat, PlayerStatus } from '/@/renderer/types';
|
||||
import { WrapperSlider } from '/@/remote/components/wrapped-slider';
|
||||
import { Tooltip } from '/@/renderer/components/tooltip';
|
||||
import { Rating } from '/@/renderer/components';
|
||||
|
||||
export const RemoteContainer = () => {
|
||||
@ -34,8 +32,6 @@ export const RemoteContainer = () => {
|
||||
[send, id],
|
||||
);
|
||||
|
||||
const debouncedSetRating = debounce(setRating, 400);
|
||||
|
||||
return (
|
||||
<>
|
||||
{song && (
|
||||
@ -56,20 +52,22 @@ export const RemoteContainer = () => {
|
||||
</Group>
|
||||
</>
|
||||
)}
|
||||
<Group
|
||||
<Grid
|
||||
grow
|
||||
spacing={0}
|
||||
align="center"
|
||||
gutter={0}
|
||||
>
|
||||
<Grid.Col span={4}>
|
||||
<RemoteButton
|
||||
tooltip="Previous track"
|
||||
variant="default"
|
||||
onClick={() => send({ event: 'previous' })}
|
||||
>
|
||||
<RiSkipBackFill size={25} />
|
||||
</RemoteButton>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4}>
|
||||
<RemoteButton
|
||||
tooltip={status === PlayerStatus.PLAYING ? 'Pause' : 'Play'}
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
if (status === PlayerStatus.PLAYING) {
|
||||
send({ event: 'pause' });
|
||||
@ -84,26 +82,38 @@ export const RemoteContainer = () => {
|
||||
<RiPlayFill size={25} />
|
||||
)}
|
||||
</RemoteButton>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={4}>
|
||||
<RemoteButton
|
||||
tooltip="Next track"
|
||||
variant="default"
|
||||
onClick={() => send({ event: 'next' })}
|
||||
>
|
||||
<RiSkipForwardFill size={25} />
|
||||
</RemoteButton>
|
||||
</Group>
|
||||
<Group
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<Grid
|
||||
grow
|
||||
spacing={0}
|
||||
align="center"
|
||||
gutter={0}
|
||||
>
|
||||
<Grid.Col
|
||||
md={3}
|
||||
span={4}
|
||||
>
|
||||
<RemoteButton
|
||||
$active={shuffle || false}
|
||||
tooltip={shuffle ? 'Shuffle tracks' : 'Shuffle disabled'}
|
||||
variant="default"
|
||||
onClick={() => send({ event: 'shuffle' })}
|
||||
>
|
||||
<RiShuffleFill size={25} />
|
||||
</RemoteButton>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
md={3}
|
||||
span={4}
|
||||
>
|
||||
<RemoteButton
|
||||
$active={repeat !== undefined && repeat !== PlayerRepeat.NONE}
|
||||
tooltip={`Repeat ${
|
||||
@ -113,7 +123,6 @@ export const RemoteContainer = () => {
|
||||
? 'all'
|
||||
: 'none'
|
||||
}`}
|
||||
variant="default"
|
||||
onClick={() => send({ event: 'repeat' })}
|
||||
>
|
||||
{repeat === undefined || repeat === PlayerRepeat.ONE ? (
|
||||
@ -122,11 +131,16 @@ export const RemoteContainer = () => {
|
||||
<RiRepeat2Line size={25} />
|
||||
)}
|
||||
</RemoteButton>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col
|
||||
md={3}
|
||||
span={4}
|
||||
>
|
||||
<RemoteButton
|
||||
$active={song?.userFavorite}
|
||||
disabled={!song}
|
||||
tooltip={song?.userFavorite ? 'Unfavorite' : 'Favorite'}
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
if (!id) return;
|
||||
|
||||
@ -135,22 +149,28 @@ export const RemoteContainer = () => {
|
||||
>
|
||||
<RiHeartLine size={25} />
|
||||
</RemoteButton>
|
||||
</Grid.Col>
|
||||
|
||||
{(song?.serverType === 'navidrome' || song?.serverType === 'subsonic') && (
|
||||
<div style={{ margin: 'auto' }}>
|
||||
<Tooltip
|
||||
label="Double click to clear"
|
||||
openDelay={1000}
|
||||
<MediaQuery
|
||||
smallerThan="md"
|
||||
styles={{ marginTop: 10 }}
|
||||
>
|
||||
<Grid.Col
|
||||
md={3}
|
||||
span={4}
|
||||
>
|
||||
<Center>
|
||||
<Rating
|
||||
sx={{ margin: 'auto' }}
|
||||
size="xl"
|
||||
value={song.userRating ?? 0}
|
||||
onChange={debouncedSetRating}
|
||||
onDoubleClick={() => debouncedSetRating(0)}
|
||||
onChange={setRating}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Center>
|
||||
</Grid.Col>
|
||||
</MediaQuery>
|
||||
)}
|
||||
</Group>
|
||||
</Grid>
|
||||
<WrapperSlider
|
||||
leftLabel={<RiVolumeUpFill size={20} />}
|
||||
max={100}
|
||||
|
@ -14,11 +14,26 @@ import { ImageButton } from '/@/remote/components/buttons/image-button';
|
||||
import { RemoteContainer } from '/@/remote/components/remote-container';
|
||||
import { ReconnectButton } from '/@/remote/components/buttons/reconnect-button';
|
||||
import { useConnected } from '/@/remote/store';
|
||||
import { NoSleepContext } from '/@/remote/context/nosleep-context';
|
||||
import NoSleep from 'nosleep.js';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { SleepButton } from '/@/remote/components/buttons/sleep-button';
|
||||
import { ResponsiveMenu } from '/@/remote/components/menu';
|
||||
|
||||
export const Shell = () => {
|
||||
const connected = useConnected();
|
||||
const noSleep = useMemo(() => {
|
||||
return new NoSleep();
|
||||
}, []);
|
||||
|
||||
const [blockSleep, setBlockSleep] = useState(false);
|
||||
|
||||
const noSleepValue = useMemo(() => {
|
||||
return { enabled: blockSleep, noSleep, setEnabled: setBlockSleep };
|
||||
}, [blockSleep, noSleep]);
|
||||
|
||||
return (
|
||||
<NoSleepContext.Provider value={noSleepValue}>
|
||||
<AppShell
|
||||
header={
|
||||
<Header height={60}>
|
||||
@ -44,7 +59,24 @@ export const Shell = () => {
|
||||
<Title ta="center">Feishin Remote</Title>
|
||||
</Grid.Col>
|
||||
</MediaQuery>
|
||||
|
||||
<MediaQuery
|
||||
largerThan="md"
|
||||
styles={{ display: 'none' }}
|
||||
>
|
||||
<Grid.Col span="auto">
|
||||
<Flex
|
||||
direction="row"
|
||||
justify="right"
|
||||
>
|
||||
<ReconnectButton />
|
||||
<ResponsiveMenu />
|
||||
</Flex>
|
||||
</Grid.Col>
|
||||
</MediaQuery>
|
||||
<MediaQuery
|
||||
smallerThan="md"
|
||||
styles={{ display: 'none' }}
|
||||
>
|
||||
<Grid.Col span="auto">
|
||||
<Flex
|
||||
direction="row"
|
||||
@ -53,8 +85,10 @@ export const Shell = () => {
|
||||
<ReconnectButton />
|
||||
<ImageButton />
|
||||
<ThemeButton />
|
||||
<SleepButton />
|
||||
</Flex>
|
||||
</Grid.Col>
|
||||
</MediaQuery>
|
||||
</Grid>
|
||||
</Header>
|
||||
}
|
||||
@ -71,5 +105,6 @@ export const Shell = () => {
|
||||
)}
|
||||
</Container>
|
||||
</AppShell>
|
||||
</NoSleepContext.Provider>
|
||||
);
|
||||
};
|
||||
|
15
src/remote/context/nosleep-context.tsx
Normal file
15
src/remote/context/nosleep-context.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
import NoSleep from 'nosleep.js';
|
||||
|
||||
export const NoSleepContext = createContext<{
|
||||
enabled: boolean;
|
||||
noSleep?: NoSleep;
|
||||
setEnabled?: (val: boolean) => void;
|
||||
}>({
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
export const useNoSleepContext = () => {
|
||||
const ctxValue = useContext(NoSleepContext);
|
||||
return ctxValue;
|
||||
};
|
28
src/remote/hooks/use-toggle-no-sleep.tsx
Normal file
28
src/remote/hooks/use-toggle-no-sleep.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useNoSleepContext } from '/@/remote/context/nosleep-context';
|
||||
import { toast } from '/@/renderer/components';
|
||||
|
||||
export const useToggleNoSleep = () => {
|
||||
const { noSleep, enabled, setEnabled } = useNoSleepContext();
|
||||
|
||||
const toggle = useCallback(async () => {
|
||||
if (!noSleep) return;
|
||||
|
||||
if (enabled) {
|
||||
noSleep.disable();
|
||||
setEnabled!(false);
|
||||
} else {
|
||||
try {
|
||||
await noSleep.enable();
|
||||
setEnabled!(true);
|
||||
} catch (error) {
|
||||
toast.error({
|
||||
message: (error as Error).message,
|
||||
title: 'Failed to disable screen lock',
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [enabled, noSleep, setEnabled]);
|
||||
|
||||
return toggle;
|
||||
};
|
Loading…
Reference in New Issue
Block a user