mirror of
https://github.com/remvze/moodist.git
synced 2025-12-18 09:24:14 +00:00
feat: add lofi music play
This commit is contained in:
parent
af096077ae
commit
fcbe50c78c
8 changed files with 246 additions and 0 deletions
56
package-lock.json
generated
56
package-lock.json
generated
|
|
@ -30,6 +30,7 @@
|
||||||
"react-hotkeys-hook": "3.2.1",
|
"react-hotkeys-hook": "3.2.1",
|
||||||
"react-icons": "4.11.0",
|
"react-icons": "4.11.0",
|
||||||
"react-wrap-balancer": "1.1.0",
|
"react-wrap-balancer": "1.1.0",
|
||||||
|
"react-youtube": "10.1.0",
|
||||||
"uuid": "10.0.0",
|
"uuid": "10.0.0",
|
||||||
"zustand": "4.4.3"
|
"zustand": "4.4.3"
|
||||||
},
|
},
|
||||||
|
|
@ -19285,6 +19286,12 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/load-script": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/load-yaml-file": {
|
"node_modules/load-yaml-file": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.2.0.tgz",
|
||||||
|
|
@ -22343,6 +22350,23 @@
|
||||||
"react": ">=16.8.0 || ^17.0.0 || ^18"
|
"react": ">=16.8.0 || ^17.0.0 || ^18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-youtube": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZfGtcVpk0SSZtWCSTYOQKhfx5/1cfyEW1JN/mugGNfAxT3rmVJeMbGpA9+e78yG21ls5nc/5uZJETE3cm3knBg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "3.1.3",
|
||||||
|
"prop-types": "15.8.1",
|
||||||
|
"youtube-player": "5.5.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.x"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=0.14.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-pkg": {
|
"node_modules/read-pkg": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz",
|
||||||
|
|
@ -23844,6 +23868,12 @@
|
||||||
"is-arrayish": "^0.3.1"
|
"is-arrayish": "^0.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sister": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/sister/-/sister-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-p19rtTs+NksBRKW9qn0UhZ8/TUI9BPw9lmtHny+Y3TinWlOa9jWh9xB0AtPSdmOy49NJJJSSe0Ey4C7h0TrcYA==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/sisteransi": {
|
"node_modules/sisteransi": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||||
|
|
@ -27593,6 +27623,32 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/youtube-player": {
|
||||||
|
"version": "5.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/youtube-player/-/youtube-player-5.5.2.tgz",
|
||||||
|
"integrity": "sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^2.6.6",
|
||||||
|
"load-script": "^1.0.0",
|
||||||
|
"sister": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/youtube-player/node_modules/debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/youtube-player/node_modules/ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "3.23.8",
|
"version": "3.23.8",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
"react-hotkeys-hook": "3.2.1",
|
"react-hotkeys-hook": "3.2.1",
|
||||||
"react-icons": "4.11.0",
|
"react-icons": "4.11.0",
|
||||||
"react-wrap-balancer": "1.1.0",
|
"react-wrap-balancer": "1.1.0",
|
||||||
|
"react-youtube": "10.1.0",
|
||||||
"uuid": "10.0.0",
|
"uuid": "10.0.0",
|
||||||
"zustand": "4.4.3"
|
"zustand": "4.4.3"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
1
src/components/modals/lofi/index.ts
Normal file
1
src/components/modals/lofi/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { LofiModal } from './lofi';
|
||||||
86
src/components/modals/lofi/lofi.module.css
Normal file
86
src/components/modals/lofi/lofi.module.css
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
.title {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-family: var(--font-heading);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice {
|
||||||
|
& p {
|
||||||
|
line-height: 1.4;
|
||||||
|
color: var(--color-foreground-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .buttons {
|
||||||
|
display: flex;
|
||||||
|
column-gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
& button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: var(--font-sm);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--color-neutral-200);
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
color: var(--color-neutral-50);
|
||||||
|
background: var(--color-neutral-950);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.videos {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
& .video {
|
||||||
|
&:not(:last-of-type) {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h2 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: var(--font-sm);
|
||||||
|
color: var(--color-foreground-subtle);
|
||||||
|
|
||||||
|
& span {
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--color-foreground-subtler);
|
||||||
|
|
||||||
|
&.index {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& strong {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .container {
|
||||||
|
padding: 8px;
|
||||||
|
background-color: var(--color-neutral-50);
|
||||||
|
border: 1px solid var(--color-neutral-300);
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
& .iframe {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
aspect-ratio: 560 / 315;
|
||||||
|
border: 1px solid var(--color-neutral-200);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/components/modals/lofi/lofi.tsx
Normal file
85
src/components/modals/lofi/lofi.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
import YouTube from 'react-youtube';
|
||||||
|
|
||||||
|
import { Modal } from '@/components/modal/modal';
|
||||||
|
|
||||||
|
import styles from './lofi.module.css';
|
||||||
|
import { padNumber } from '@/helpers/number';
|
||||||
|
|
||||||
|
interface LofiProps {
|
||||||
|
onClose: () => void;
|
||||||
|
show: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const videos = [
|
||||||
|
{
|
||||||
|
channel: 'Lofi Girl',
|
||||||
|
id: 'jfKfPfyJRdk',
|
||||||
|
title: 'lofi hip hop radio',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channel: 'Lofi Girl',
|
||||||
|
id: '4xDzrJKXOOY',
|
||||||
|
title: 'synthwave radio',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channel: 'Lofi Girl',
|
||||||
|
id: 'P6Segk8cr-c',
|
||||||
|
title: 'sad lofi radio',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channel: 'Lofi Girl',
|
||||||
|
id: 'S_MOd40zlYU',
|
||||||
|
title: 'dark ambient radio',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channel: 'Lofi Girl',
|
||||||
|
id: 'TtkFsfOP9QI',
|
||||||
|
title: 'peaceful piano radio',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function LofiModal({ onClose, show }: LofiProps) {
|
||||||
|
const [isAccepted, setIsAccepted] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal persist show={show} onClose={onClose}>
|
||||||
|
<h1 className={styles.title}>Lofi Music Player</h1>
|
||||||
|
|
||||||
|
{!isAccepted ? (
|
||||||
|
<div className={styles.notice}>
|
||||||
|
<p>
|
||||||
|
This feature plays music using embedded YouTube videos. By
|
||||||
|
continuing, you agree to connect to YouTube, which may collect data
|
||||||
|
in accordance with their privacy policy. We do not control or track
|
||||||
|
this data.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className={styles.buttons}>
|
||||||
|
<button onClick={onClose}>Cancel</button>
|
||||||
|
<button
|
||||||
|
className={styles.primary}
|
||||||
|
onClick={() => setIsAccepted(true)}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={styles.videos}>
|
||||||
|
{videos.map((video, index) => (
|
||||||
|
<div className={styles.video} key={video.id}>
|
||||||
|
<h2>
|
||||||
|
<span className={styles.index}>{padNumber(index + 1, 2)}</span>{' '}
|
||||||
|
<strong>{video.channel}</strong> <span>/</span> {video.title}
|
||||||
|
</h2>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<YouTube iframeClassName={styles.iframe} videoId={video.id} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -12,3 +12,4 @@ export { Todo as TodoItem } from './todo';
|
||||||
export { Countdown as CountdownItem } from './countdown';
|
export { Countdown as CountdownItem } from './countdown';
|
||||||
export { Binaural as BinauralItem } from './binaural';
|
export { Binaural as BinauralItem } from './binaural';
|
||||||
export { Isochronic as IsochronicItem } from './isochronic';
|
export { Isochronic as IsochronicItem } from './isochronic';
|
||||||
|
export { Lofi as LofiItem } from './lofi';
|
||||||
|
|
|
||||||
11
src/components/toolbar/menu/items/lofi.tsx
Normal file
11
src/components/toolbar/menu/items/lofi.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { FaHeadphonesAlt } from 'react-icons/fa/index';
|
||||||
|
|
||||||
|
import { Item } from '../item';
|
||||||
|
|
||||||
|
interface LofiProps {
|
||||||
|
open: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Lofi({ open }: LofiProps) {
|
||||||
|
return <Item icon={<FaHeadphonesAlt />} label="Lofi Music" onClick={open} />;
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ import {
|
||||||
CountdownItem,
|
CountdownItem,
|
||||||
BinauralItem,
|
BinauralItem,
|
||||||
IsochronicItem,
|
IsochronicItem,
|
||||||
|
LofiItem,
|
||||||
} from './items';
|
} from './items';
|
||||||
import { Divider } from './divider';
|
import { Divider } from './divider';
|
||||||
import { ShareLinkModal } from '@/components/modals/share-link';
|
import { ShareLinkModal } from '@/components/modals/share-link';
|
||||||
|
|
@ -28,6 +29,7 @@ import { SleepTimerModal } from '@/components/modals/sleep-timer';
|
||||||
import { BreathingExerciseModal } from '@/components/modals/breathing';
|
import { BreathingExerciseModal } from '@/components/modals/breathing';
|
||||||
import { BinauralModal } from '@/components/modals/binaural';
|
import { BinauralModal } from '@/components/modals/binaural';
|
||||||
import { IsochronicModal } from '@/components/modals/isochronic';
|
import { IsochronicModal } from '@/components/modals/isochronic';
|
||||||
|
import { LofiModal } from '@/components/modals/lofi';
|
||||||
import { Pomodoro, Notepad, Todo, Countdown } from '@/components/toolbox';
|
import { Pomodoro, Notepad, Todo, Countdown } from '@/components/toolbox';
|
||||||
import { Slider } from '@/components/slider';
|
import { Slider } from '@/components/slider';
|
||||||
|
|
||||||
|
|
@ -51,6 +53,7 @@ export function Menu() {
|
||||||
breathing: false,
|
breathing: false,
|
||||||
countdown: false,
|
countdown: false,
|
||||||
isochronic: false,
|
isochronic: false,
|
||||||
|
lofi: false,
|
||||||
notepad: false,
|
notepad: false,
|
||||||
pomodoro: false,
|
pomodoro: false,
|
||||||
presets: false,
|
presets: false,
|
||||||
|
|
@ -137,6 +140,7 @@ export function Menu() {
|
||||||
<Divider />
|
<Divider />
|
||||||
<BinauralItem open={() => open('binaural')} />
|
<BinauralItem open={() => open('binaural')} />
|
||||||
<IsochronicItem open={() => open('isochronic')} />
|
<IsochronicItem open={() => open('isochronic')} />
|
||||||
|
<LofiItem open={() => open('lofi')} />
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<ShortcutsItem open={() => open('shortcuts')} />
|
<ShortcutsItem open={() => open('shortcuts')} />
|
||||||
|
|
@ -193,6 +197,7 @@ export function Menu() {
|
||||||
show={modals.isochronic}
|
show={modals.isochronic}
|
||||||
onClose={() => close('isochronic')}
|
onClose={() => close('isochronic')}
|
||||||
/>
|
/>
|
||||||
|
<LofiModal show={modals.lofi} onClose={() => close('lofi')} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue