25 Commits

Author SHA1 Message Date
MAZE
b8bc9c8b4c chore(release): 1.2.0 2024-01-04 20:19:35 +03:30
MAZE
ee606139a8 chore: update GitHub action 2024-01-04 20:18:45 +03:30
MAZE
7823dc7ff4 style: add animation to modal 2024-01-04 19:58:16 +03:30
MAZE
37a0736a0e style: widen the menu 2024-01-04 19:54:38 +03:30
MAZE
c51acd6261 style: change copy 2024-01-04 19:54:06 +03:30
MAZE
ff26597d22 feat: add disabled state 2024-01-04 19:51:58 +03:30
MAZE
c8e51226e5 style: change to primary color 2024-01-03 20:03:32 +03:30
MAZE
131ab29621 style: add icon to menu items 2024-01-03 20:01:58 +03:30
MAZE
0f62f0795c feat: implement override feature 2024-01-03 18:42:06 +03:30
MAZE
1a23e004a6 fix: stringify dependency 2024-01-03 14:14:01 +03:30
MAZE
93ff72a052 feat: implement sharing URL 2024-01-03 00:03:58 +03:30
MAZE
ef81f198ba feat: basic structure for share link 2024-01-02 22:20:59 +03:30
MAZE
35e32152b1 feat: add share modal 2024-01-02 19:48:57 +03:30
MAZE
26bf01690c refactor: better item structure for menu 2024-01-02 18:35:06 +03:30
MAZE
fe2357c995 feat: add share placeholder 2024-01-02 17:35:10 +03:30
MAZE
85b627ecb9 style: change border color 2024-01-02 16:58:41 +03:30
MAZE
8beb42cb1b refactor: rewrite menu with floating ui 2024-01-02 16:54:29 +03:30
MAZE
17027e299b feat: add animation to menu box 2024-01-02 16:28:18 +03:30
MAZE
184bb09f5a feat: add menu button 2024-01-02 16:24:55 +03:30
MAZE
660ee07a23 chore: change docker-compose file 2024-01-01 12:42:56 +03:30
MAZE
cb4bfea5ab chore: change docker workflow 2024-01-01 12:39:22 +03:30
MAZE ✧
210bd234e0 Merge pull request #3 from baldator/main
Add github action to build docker image and docker-compose support
2024-01-01 12:32:17 +03:30
marco
64ef5c5138 . 2023-12-31 13:43:27 +00:00
MAZE ✧
1218751a6f Merge pull request #2 from javigomezo/main
feat(docker): add dockerfile
2023-12-31 12:57:16 +03:30
Javier Gómez
a234bc17a6 feat(docker): add dockerfile 2023-12-30 23:07:10 +01:00
31 changed files with 814 additions and 6 deletions

34
.github/workflows/build_docker.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Build and push main image
on:
push:
tags:
- '*'
jobs:
push-store-image:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@main
- name: 'Login to GitHub Container Registry'
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{github.actor}}
password: ${{secrets.ACCESS_TOKEN}}
- name: 'Build Inventory Image'
run: |
IMAGE_NAME="ghcr.io/remvze/moodist"
GIT_TAG=${{ github.ref }}
GIT_TAG=${GIT_TAG#refs/tags/}
docker build . --tag $IMAGE_NAME:latest
docker push $IMAGE_NAME:latest
docker build . --tag $IMAGE_NAME:$GIT_TAG
docker push $IMAGE_NAME:$GIT_TAG

View File

@@ -2,6 +2,49 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [1.2.0](https://github.com/remvze/moodist/compare/v1.1.0...v1.2.0) (2024-01-04)
### ♻️ Code Refactoring
* better item structure for menu ([26bf016](https://github.com/remvze/moodist/commit/26bf01690cfcc105b661951bcb2347394a67fb68))
* rewrite menu with floating ui ([8beb42c](https://github.com/remvze/moodist/commit/8beb42cb1b92c99aa9656b35cd7d82094e5baf72))
### 🐛 Bug Fixes
* stringify dependency ([1a23e00](https://github.com/remvze/moodist/commit/1a23e004a65960ce169990211f150db25762fead))
### ✨ Features
* add animation to menu box ([17027e2](https://github.com/remvze/moodist/commit/17027e299bb9bf958aebaf735c40e7664ad71e8b))
* add disabled state ([ff26597](https://github.com/remvze/moodist/commit/ff26597d22d444d18d2874a5c278eccc288972de))
* add menu button ([184bb09](https://github.com/remvze/moodist/commit/184bb09f5ab09fcf877e6a904023d9de72be9a89))
* add share modal ([35e3215](https://github.com/remvze/moodist/commit/35e32152b153f4dfaf9e071f526f6d7602ea97fc))
* add share placeholder ([fe2357c](https://github.com/remvze/moodist/commit/fe2357c995713cd0fb8335b325266859dc47a769))
* basic structure for share link ([ef81f19](https://github.com/remvze/moodist/commit/ef81f198baeb927e3b1768570f75e6638a7bd0b6))
* **docker:** add dockerfile ([a234bc1](https://github.com/remvze/moodist/commit/a234bc17a66331acbbc1d980cd1f53d58646f534))
* implement override feature ([0f62f07](https://github.com/remvze/moodist/commit/0f62f0795c5a9e06fa4e62b6b7b1e6c0774dfe0f))
* implement sharing URL ([93ff72a](https://github.com/remvze/moodist/commit/93ff72a052484b36c9ac821b94b632865b4a3550))
### 💄 Styling
* add animation to modal ([7823dc7](https://github.com/remvze/moodist/commit/7823dc7ff473278ef8ee401e69796c17b33da794))
* add icon to menu items ([131ab29](https://github.com/remvze/moodist/commit/131ab296215812e45a0c60486d75683f3de25d16))
* change border color ([85b627e](https://github.com/remvze/moodist/commit/85b627ecb96a4f52ecacdb53ed4484c050adba5e))
* change copy ([c51acd6](https://github.com/remvze/moodist/commit/c51acd62618cc705902dc01f0574a2c9124264c5))
* change to primary color ([c8e5122](https://github.com/remvze/moodist/commit/c8e51226e57bfa72ad91318de25fc5f9b5751634))
* widen the menu ([37a0736](https://github.com/remvze/moodist/commit/37a0736a0e7edd09c33940099c884e5b48afbbf1))
### 🚚 Chores
* change docker workflow ([cb4bfea](https://github.com/remvze/moodist/commit/cb4bfea5ab4326dee17c78554f12a08ffcb9dd0e))
* change docker-compose file ([660ee07](https://github.com/remvze/moodist/commit/660ee07a2359ec77c9d56bbe552541246e0f79c5))
* update GitHub action ([ee60613](https://github.com/remvze/moodist/commit/ee606139a80121fd6ee1b8233f82af994c4e1178))
## [1.1.0](https://github.com/remvze/moodist/compare/v1.0.0...v1.1.0) (2023-12-29)

12
Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
FROM node:20-alpine3.18 AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine AS runtime
COPY ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 8080

10
docker-compose.yml Normal file
View File

@@ -0,0 +1,10 @@
version: '3.9'
services:
moodist:
image: ghcr.io/remvze/moodist
logging:
options:
max-size: 1g
restart: always
ports:
- '8080:8080'

31
docker/nginx/nginx.conf Normal file
View File

@@ -0,0 +1,31 @@
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
index index.html index.htm;
include /etc/nginx/mime.types;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
error_page 404 /404.html;
location = /404.html {
root /usr/share/nginx/html;
internal;
}
location / {
try_files $uri $uri/index.html =404;
}
}
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "moodist",
"version": "1.1.0",
"version": "1.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "moodist",
"version": "1.1.0",
"version": "1.2.0",
"dependencies": {
"@astrojs/react": "^3.0.3",
"@floating-ui/react": "0.26.0",

View File

@@ -1,7 +1,7 @@
{
"name": "moodist",
"type": "module",
"version": "1.1.0",
"version": "1.2.0",
"scripts": {
"dev": "astro dev",
"start": "astro dev",

Binary file not shown.

View File

@@ -9,7 +9,8 @@ import { StoreConsumer } from '@/components/store-consumer';
import { Buttons } from '@/components/buttons';
import { Categories } from '@/components/categories';
import { ScrollToTop } from '@/components/scroll-to-top';
import { Shuffle } from '@/components/shuffle';
import { SharedModal } from '@/components/modals/shared';
import { Menu } from '@/components/menu/menu';
import { SnackbarProvider } from '@/contexts/snackbar';
import { sounds } from '@/data/sounds';
@@ -60,7 +61,8 @@ export function App() {
</Container>
<ScrollToTop />
<Shuffle />
<Menu />
<SharedModal />
</StoreConsumer>
</SnackbarProvider>
);

View File

@@ -0,0 +1 @@
export { Menu } from './menu';

View File

@@ -0,0 +1 @@
export { Item } from './item';

View File

@@ -0,0 +1,33 @@
.item {
display: flex;
column-gap: 8px;
align-items: center;
justify-content: flex-start;
width: 100%;
padding: 16px 12px;
font-size: var(--font-sm);
font-weight: 500;
line-height: 1;
color: var(--color-foreground-subtle);
text-align: left;
cursor: pointer;
background-color: transparent;
border: 1px solid var(--color-neutral-200);
border-radius: 4px;
outline: none;
transition: 0.2s;
&:disabled {
cursor: not-allowed;
opacity: 0.4;
}
&:not(:disabled):hover {
color: var(--color-foreground);
background-color: var(--color-neutral-200);
}
& .icon {
color: var(--color-foreground);
}
}

View File

@@ -0,0 +1,16 @@
import styles from './item.module.css';
interface ItemProps {
disabled: boolean;
icon: React.ReactElement;
label: string;
onClick: () => void;
}
export function Item({ disabled = false, icon, label, onClick }: ItemProps) {
return (
<button className={styles.item} disabled={disabled} onClick={onClick}>
<span className={styles.icon}>{icon}</span> {label}
</button>
);
}

View File

@@ -0,0 +1,2 @@
export { Shuffle as ShuffleItem } from './shuffle';
export { Share as ShareItem } from './share';

View File

@@ -0,0 +1,22 @@
import { IoShareSocialSharp } from 'react-icons/io5/index';
import { Item } from '../item';
import { useSoundStore } from '@/store';
interface ShareProps {
open: () => void;
}
export function Share({ open }: ShareProps) {
const noSelected = useSoundStore(state => state.noSelected());
return (
<Item
disabled={noSelected}
icon={<IoShareSocialSharp />}
label="Share Sounds"
onClick={open}
/>
);
}

View File

@@ -0,0 +1,11 @@
import { BiShuffle } from 'react-icons/bi/index';
import { useSoundStore } from '@/store';
import { Item } from '../item';
export function Shuffle() {
const shuffle = useSoundStore(state => state.shuffle);
return <Item icon={<BiShuffle />} label="Shuffle Sounds" onClick={shuffle} />;
}

View File

@@ -0,0 +1,57 @@
.wrapper {
position: fixed;
right: 20px;
bottom: 20px;
z-index: 5;
& .menuButton {
display: flex;
align-items: center;
justify-content: center;
width: 45px;
height: 45px;
font-size: var(--font-md);
color: var(--color-foreground);
cursor: pointer;
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300);
border-radius: 50%;
transition: 0.2s;
&:hover {
background-color: var(--color-neutral-200);
}
}
& .menu {
display: flex;
flex-direction: column;
row-gap: 4px;
width: 240px;
padding: 4px;
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300);
border-radius: 4px;
& .menuItem {
position: flex;
align-items: center;
width: 100%;
padding: 12px 8px;
font-size: var(--font-sm);
font-weight: 500;
color: var(--color-foreground-subtle);
cursor: pointer;
background-color: transparent;
border: 1px solid var(--color-neutral-200);
border-radius: 4px;
outline: none;
transition: 0.2s;
&:hover {
color: var(--color-foreground);
background-color: var(--color-neutral-200);
}
}
}
}

View File

@@ -0,0 +1,92 @@
import { useState } from 'react';
import { IoMenu, IoClose } from 'react-icons/io5/index';
import { AnimatePresence, motion } from 'framer-motion';
import {
useFloating,
autoUpdate,
offset,
flip,
shift,
useClick,
useDismiss,
useRole,
useInteractions,
FloatingFocusManager,
} from '@floating-ui/react';
import { ShuffleItem, ShareItem } from './items';
import { ShareLinkModal } from '@/components/modals/share-link';
import { slideY, fade, mix } from '@/lib/motion';
import styles from './menu.module.css';
export function Menu() {
const [isOpen, setIsOpen] = useState(false);
const [showShareLink, setShowShareLink] = useState(false);
const variants = mix(slideY(-20), fade());
const { context, floatingStyles, refs } = useFloating({
middleware: [offset(12), flip(), shift()],
onOpenChange: setIsOpen,
open: isOpen,
placement: 'top-end',
whileElementsMounted: autoUpdate,
});
const click = useClick(context);
const dismiss = useDismiss(context);
const role = useRole(context);
const { getFloatingProps, getReferenceProps } = useInteractions([
click,
dismiss,
role,
]);
return (
<>
<div className={styles.wrapper}>
<button
aria-label="Menu"
className={styles.menuButton}
ref={refs.setReference}
onClick={() => setIsOpen(prev => !prev)}
{...getReferenceProps()}
>
{isOpen ? <IoClose /> : <IoMenu />}
</button>
<AnimatePresence>
{isOpen && (
<FloatingFocusManager context={context} modal={false}>
<div
ref={refs.setFloating}
style={floatingStyles}
{...getFloatingProps()}
>
<motion.div
animate="show"
className={styles.menu}
exit="hidden"
initial="hidden"
variants={variants}
>
<ShareItem open={() => setShowShareLink(true)} />
<ShuffleItem />
</motion.div>
</div>
</FloatingFocusManager>
)}
</AnimatePresence>
</div>
<ShareLinkModal
show={showShareLink}
onClose={() => setShowShareLink(false)}
/>
</>
);
}

View File

@@ -0,0 +1 @@
export { Modal } from './modal';

View File

@@ -0,0 +1,49 @@
.overlay {
position: fixed;
inset: 0;
z-index: 10;
background-color: rgb(9 9 11 / 40%);
backdrop-filter: blur(5px);
}
.modal {
position: fixed;
top: 50%;
left: 50%;
z-index: 12;
width: 100%;
max-height: 100%;
padding: 50px 0;
overflow-y: auto;
pointer-events: none;
transform: translate(-50%, -50%);
& .content {
position: relative;
width: 90%;
max-width: 500px;
padding: 20px;
padding-top: 40px;
margin: 0 auto;
pointer-events: fill;
background-color: var(--color-neutral-100);
border-radius: 8px;
& .close {
position: absolute;
top: 10px;
right: 10px;
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
font-size: 16px;
color: var(--color-foreground-subtle);
cursor: pointer;
background-color: transparent;
border: none;
outline: none;
}
}
}

View File

@@ -0,0 +1,52 @@
import { AnimatePresence, motion } from 'framer-motion';
import { IoClose } from 'react-icons/io5/index';
import { fade, mix, slideY } from '@/lib/motion';
import styles from './modal.module.css';
interface ModalProps {
children: React.ReactNode;
onClose: () => void;
show: boolean;
}
export function Modal({ children, onClose, show }: ModalProps) {
const variants = {
modal: mix(fade(), slideY(20)),
overlay: fade(),
};
return (
<AnimatePresence>
{show && (
<>
<motion.div
animate="show"
className={styles.overlay}
exit="hidden"
initial="hidden"
variants={variants.overlay}
onClick={onClose}
onKeyDown={onClose}
/>
<div className={styles.modal}>
<motion.div
animate="show"
className={styles.content}
exit="hidden"
initial="hidden"
variants={variants.modal}
>
<button className={styles.close} onClick={onClose}>
<IoClose />
</button>
{children}
</motion.div>
</div>
</>
)}
</AnimatePresence>
);
}

View File

@@ -0,0 +1 @@
export { ShareLinkModal } from './share-link';

View File

@@ -0,0 +1,53 @@
.heading {
font-family: var(--font-heading);
font-size: var(--font-md);
font-weight: 700;
}
.desc {
margin-top: 12px;
line-height: 1.6;
color: var(--color-foreground-subtle);
}
.inputWrapper {
display: flex;
align-items: center;
width: 100%;
height: 45px;
padding: 4px;
margin-top: 12px;
background-color: var(--color-neutral-50);
border: 1px solid var(--color-neutral-200);
border-radius: 4px;
& input {
flex-grow: 1;
height: 100%;
padding: 0 10px;
font-size: var(--font-sm);
color: var(--color-foreground);
background: transparent;
border: none;
outline: none;
}
& button {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
aspect-ratio: 1 / 1;
color: var(--color-foreground);
cursor: pointer;
background-color: var(--color-neutral-100);
border: none;
border-radius: 4px;
outline: none;
transition: 0.2s;
&:hover {
background-color: var(--color-neutral-200);
}
}
}

View File

@@ -0,0 +1,67 @@
import { useMemo, useEffect, useState } from 'react';
import { IoCopyOutline, IoCheckmark } from 'react-icons/io5/index';
import { Modal } from '@/components/modal';
import { useCopy } from '@/hooks/use-copy';
import { useSoundStore } from '@/store';
import styles from './share-link.module.css';
interface ShareLinkModalProps {
onClose: () => void;
show: boolean;
}
export function ShareLinkModal({ onClose, show }: ShareLinkModalProps) {
const [isMounted, setIsMounted] = useState(false);
const sounds = useSoundStore(state => state.sounds);
const { copy, copying } = useCopy();
const selected = useMemo(() => {
return Object.keys(sounds)
.map(sound => ({
id: sound,
isSelected: sounds[sound].isSelected,
volume: sounds[sound].volume.toFixed(1),
}))
.filter(sound => sound.isSelected);
}, [sounds, JSON.stringify(sounds)]); // eslint-disable-line
const string = useMemo(() => {
const object: Record<string, number> = {};
selected.forEach(sound => {
object[sound.id] = Number(sound.volume);
});
return JSON.stringify(object);
}, [selected]);
const url = useMemo(() => {
if (!isMounted)
return `https://moodist.app/?share=${encodeURIComponent(string)}`;
return `${window.location.protocol}//${
window.location.host
}/?share=${encodeURIComponent(string)}`;
}, [string, isMounted]);
useEffect(() => setIsMounted(true), []);
return (
<Modal show={show} onClose={onClose}>
<h1 className={styles.heading}>Share your sound selection!</h1>
<p className={styles.desc}>
Copy and send the following link to the person you want to share your
selection with.
</p>
<div className={styles.inputWrapper}>
<input readOnly type="text" value={url} />
<button onClick={() => copy(url)}>
{copying ? <IoCheckmark /> : <IoCopyOutline />}
</button>
</div>
</Modal>
);
}

View File

@@ -0,0 +1 @@
export { SharedModal } from './shared';

View File

@@ -0,0 +1,68 @@
.heading {
font-family: var(--font-heading);
font-size: var(--font-md);
font-weight: 700;
}
.desc {
margin-top: 12px;
line-height: 1.6;
color: var(--color-foreground-subtle);
}
.sounds {
display: flex;
flex-wrap: wrap;
gap: 8px;
width: 100%;
padding: 12px;
margin-top: 12px;
background-color: var(--color-neutral-50);
border: 1px solid var(--color-neutral-200);
border-radius: 4px;
& .sound {
padding: 8px 16px;
font-size: var(--font-sm);
font-weight: 500;
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-200);
border-radius: 100px;
}
}
.footer {
display: flex;
column-gap: 8px;
align-items: center;
justify-content: flex-end;
margin-top: 12px;
.button {
padding: 12px 16px;
font-family: var(--font-heading);
font-size: var(--font-sm);
font-weight: 600;
color: var(--color-foreground-subtle);
cursor: pointer;
background-color: var(--color-neutral-200);
border: none;
border-radius: 4px;
outline: none;
transition: 0.2s;
&:hover {
color: var(--color-foreground);
background-color: var(--color-neutral-300);
}
&.primary {
color: var(--color-neutral-200);
background-color: var(--color-neutral-950);
&:hover {
background-color: var(--color-neutral-800);
}
}
}
}

View File

@@ -0,0 +1,107 @@
import { useState, useEffect } from 'react';
import { Modal } from '@/components/modal';
import { useSoundStore } from '@/store';
import { useSnackbar } from '@/contexts/snackbar';
import { cn } from '@/helpers/styles';
import { sounds } from '@/data/sounds';
import styles from './shared.module.css';
export function SharedModal() {
const override = useSoundStore(state => state.override);
const showSnackbar = useSnackbar();
const [isOpen, setIsOpen] = useState(false);
const [sharedSounds, setSharedSounds] = useState<
Array<{
id: string;
label: string;
volume: number;
}>
>([]);
useEffect(() => {
const searchParams = new URLSearchParams(window.location.search);
const share = searchParams.get('share');
if (share) {
try {
const parsed = JSON.parse(decodeURIComponent(share));
const allSounds: Record<string, string> = {};
sounds.categories.forEach(category => {
category.sounds.forEach(sound => {
allSounds[sound.id] = sound.label;
});
});
const _sharedSounds: Array<{
id: string;
label: string;
volume: number;
}> = [];
Object.keys(parsed).forEach(sound => {
if (allSounds[sound]) {
_sharedSounds.push({
id: sound,
label: allSounds[sound],
volume: Number(parsed[sound]),
});
}
});
if (_sharedSounds.length) {
setIsOpen(true);
setSharedSounds(_sharedSounds);
}
} catch (error) {
return;
} finally {
history.pushState({}, '', location.href.split('?')[0]);
}
}
}, []);
const handleOverride = () => {
const newSounds: Record<string, number> = {};
sharedSounds.forEach(sound => {
newSounds[sound.id] = sound.volume;
});
override(newSounds);
setIsOpen(false);
showSnackbar('Done! You can now play the new selection.');
};
return (
<Modal show={isOpen} onClose={() => setIsOpen(false)}>
<h1 className={styles.heading}>New sound mix detected!</h1>
<p className={styles.desc}>
Someone has shared the following mix with you. Would you want to
override your current selection?
</p>
<div className={styles.sounds}>
{sharedSounds.map(sound => (
<div className={styles.sound} key={sound.id}>
{sound.label}
</div>
))}
</div>
<div className={styles.footer}>
<button className={cn(styles.button)} onClick={() => setIsOpen(false)}>
Cancel
</button>
<button
className={cn(styles.button, styles.primary)}
onClick={handleOverride}
>
Override
</button>
</div>
</Modal>
);
}

View File

@@ -44,7 +44,7 @@
width: 8px;
height: 8px;
content: '';
background-color: #34d399;
background-color: var(--color-neutral-950);
border-radius: 50%;
}
}

19
src/hooks/use-copy.ts Normal file
View File

@@ -0,0 +1,19 @@
import { useState, useCallback } from 'react';
export function useCopy(timeout = 1500) {
const [copying, setCopying] = useState(false);
const copy = useCallback(
(content: string) => {
if (copying) return;
navigator.clipboard.writeText(content);
setCopying(true);
setTimeout(() => setCopying(false), timeout);
},
[copying, timeout],
);
return { copy, copying };
}

View File

@@ -5,6 +5,7 @@ import type { SoundState } from './sound.state';
import { pickMany, random } from '@/helpers/random';
export interface SoundActions {
override: (sounds: Record<string, number>) => void;
pause: () => void;
play: () => void;
restoreHistory: () => void;
@@ -24,6 +25,19 @@ export const createActions: StateCreator<
SoundActions
> = (set, get) => {
return {
override(newSounds) {
get().unselectAll();
const sounds = get().sounds;
Object.keys(newSounds).forEach(sound => {
sounds[sound].isSelected = true;
sounds[sound].volume = newSounds[sound];
});
set({ sounds: { ...sounds } });
},
pause() {
set({ isPlaying: false });
},

View File

@@ -33,3 +33,12 @@
src: url('/fonts/inter-tight-v7-latin-600.woff2') format('woff2');
font-display: swap;
}
/* inter-tight-700 - latin */
@font-face {
font-family: 'Inter Tight';
font-style: normal;
font-weight: 700;
src: url('/fonts/inter-tight-v7-latin-700.woff2') format('woff2');
font-display: swap;
}