69 Commits

Author SHA1 Message Date
MAZE
75ff67c9e6 chore(release): 1.3.1 2024-02-01 23:40:29 +03:30
MAZE
1f806c4e56 chore: add donation link to README file 2024-02-01 23:39:59 +03:30
MAZE
e6f768a5e6 fix: complete donation links 2024-02-01 23:38:55 +03:30
MAZE
8e0291004a fix: coffee typo 2024-02-01 23:27:06 +03:30
MAZE
d449c29321 feat: add donate section 2024-02-01 22:23:49 +03:30
MAZE
f12ca4806c feat: add donate item 2024-02-01 22:05:42 +03:30
MAZE
17b4f25ff1 feat: add donation header 2024-02-01 21:59:08 +03:30
MAZE
f877e49763 chore(release): 1.3.0 2024-02-01 20:36:46 +03:30
MAZE
f1d212abc8 chore: add binaural beats 2024-02-01 20:16:47 +03:30
MAZE
38f6f7dbe6 chore: add more sounds 2024-01-30 15:29:55 +03:30
MAZE
937bf29d09 chore: add more sounds 2024-01-30 00:04:59 +03:30
MAZE
e2172fd2bb chore: add more sounds 2024-01-29 23:49:07 +03:30
MAZE
1f12afa394 chore: add more sounds 2024-01-29 23:36:44 +03:30
MAZE
d96461d1ea fix: remove fading 2024-01-29 19:16:46 +03:30
MAZE
5467bbbc24 feat: add fading to intro and outro 2024-01-29 19:01:36 +03:30
MAZE
32da26ccfc fix: undo changes 2024-01-28 19:39:57 +03:30
MAZE
81f33d9d37 fix: add media session 2024-01-28 19:33:52 +03:30
MAZE
463667c868 fix: connect audio context to audio element 2024-01-28 19:28:55 +03:30
MAZE
b77c817db2 refactor: remove unmute and media session 2024-01-28 15:45:27 +03:30
MAZE
216b913ccd fix: add media session 2024-01-28 15:40:51 +03:30
MAZE
e422b52436 fix: add unmute for iOS 2024-01-28 15:35:09 +03:30
MAZE
1f635348e3 refactor: remove media session 2024-01-28 15:23:58 +03:30
MAZE
889962babe fix: add audio element 2024-01-28 15:15:30 +03:30
MAZE
5e0a84259f feat: add media session 2024-01-28 15:05:13 +03:30
MAZE
8e4d0531e0 fix: resume audio 2024-01-28 14:44:31 +03:30
MAZE
cd05704a73 chore: add more sounds 2024-01-22 19:58:06 +03:30
MAZE
01b4bdbb57 chore: add more sounds 2024-01-22 18:31:23 +03:30
MAZE
f682a910da feat: add loader for favorites 2024-01-09 16:09:07 +03:30
MAZE
a33ae450cf fix: increase decimal 2024-01-09 15:59:57 +03:30
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
MAZE
69b85199bb chore(release): 1.1.0 2023-12-29 17:32:03 +03:30
MAZE
2c74dd0d60 style: add hover states 2023-12-29 17:30:13 +03:30
MAZE
8efb1cee00 style: change button style 2023-12-29 17:22:41 +03:30
MAZE
bd517f88c0 style: change theme 2023-12-29 17:15:34 +03:30
MAZE
00fc5f3a87 style: change sound counter 2023-12-28 22:44:53 +03:30
MAZE
37bad8149e chore: relocate underwater audio 2023-12-28 22:19:23 +03:30
MAZE
c1c3945d43 chore: add transport category 2023-12-28 22:16:28 +03:30
MAZE
5970012fa6 chore: add places category 2023-12-28 22:09:02 +03:30
MAZE
121a8f204c chore: remove heartbeat audio 2023-12-28 21:53:16 +03:30
MAZE
f43a378697 chore: change heartbeat audio 2023-12-28 21:45:20 +03:30
MAZE
28c3c404ad fix: change icon path 2023-12-28 00:06:19 +03:30
MAZE
e3864bede1 chore: add more sounds 2023-12-28 00:02:50 +03:30
MAZE
55e7f05892 chore: add more sounds 2023-12-27 23:40:29 +03:30
MAZE
318e87c9f1 chore: add more sounds 2023-12-27 16:55:22 +03:30
MAZE
a43c679e21 refactor: change ordering config 2023-12-10 15:37:37 +03:30
116 changed files with 1684 additions and 258 deletions

View File

@@ -45,6 +45,7 @@
"sort-keys-fix/sort-keys-fix": ["warn", "asc"],
"sort-destructure-keys/sort-destructure-keys": "warn",
"jsx-a11y/no-static-element-interactions": "off",
"jsx-a11y/media-has-caption": "off",
"react/jsx-sort-props": [
"warn",
{

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

@@ -1,7 +1,7 @@
{
"extends": [
"stylelint-config-standard",
"stylelint-config-idiomatic-order",
"stylelint-config-recess-order",
"stylelint-config-html",
"stylelint-prettier/recommended"
],

View File

@@ -11,8 +11,8 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit"
},
"[javascript][javascriptreact][typescript][typescriptreact][astro]": {
"editor.formatOnSave": false

View File

@@ -2,6 +2,140 @@
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.3.1](https://github.com/remvze/moodist/compare/v1.3.0...v1.3.1) (2024-02-01)
### ✨ Features
* add donate item ([f12ca48](https://github.com/remvze/moodist/commit/f12ca4806c9279f69f298bef770f8cac69a0860a))
* add donate section ([d449c29](https://github.com/remvze/moodist/commit/d449c29321024a43517e92cc59223b4b22fe2e82))
* add donation header ([17b4f25](https://github.com/remvze/moodist/commit/17b4f25ff10e09a917203e67cf963cac8358de1a))
### 🐛 Bug Fixes
* coffee typo ([8e02910](https://github.com/remvze/moodist/commit/8e0291004a90e55b67a921b9ffb483b409109ae4))
* complete donation links ([e6f768a](https://github.com/remvze/moodist/commit/e6f768a5e6dc983ae04b70f6c434fd4c13aeb506))
### 🚚 Chores
* add donation link to README file ([1f806c4](https://github.com/remvze/moodist/commit/1f806c4e561d79a00850130eda09376299d85ed2))
## [1.3.0](https://github.com/remvze/moodist/compare/v1.2.0...v1.3.0) (2024-02-01)
### ♻️ Code Refactoring
* remove media session ([1f63534](https://github.com/remvze/moodist/commit/1f635348e3e5cf73ee76e1c5fac7b5f5b7f7ea6a))
* remove unmute and media session ([b77c817](https://github.com/remvze/moodist/commit/b77c817db25e1a738b6770b1ae86d792e0d42240))
### ✨ Features
* add fading to intro and outro ([5467bbb](https://github.com/remvze/moodist/commit/5467bbbc2437a5504e157122a995ad7a565ff0b8))
* add loader for favorites ([f682a91](https://github.com/remvze/moodist/commit/f682a910da97eb53cfb90ce955e953f05088e686))
* add media session ([5e0a842](https://github.com/remvze/moodist/commit/5e0a84259ff5586700c4e10087485d905be7ccee))
### 🐛 Bug Fixes
* add audio element ([889962b](https://github.com/remvze/moodist/commit/889962babe6e940ff283a41b145620d2a0477c70))
* add media session ([81f33d9](https://github.com/remvze/moodist/commit/81f33d9d375f63b4dd0bf58ad28a72354d85706e))
* add media session ([216b913](https://github.com/remvze/moodist/commit/216b913ccd0a7dfe0d03575f842aac9711ef0216))
* add unmute for iOS ([e422b52](https://github.com/remvze/moodist/commit/e422b52436c7dfc0b6cf866afa2b74dc219dcf2f))
* connect audio context to audio element ([463667c](https://github.com/remvze/moodist/commit/463667c868371540c46c9007e686961f9a4be7e5))
* increase decimal ([a33ae45](https://github.com/remvze/moodist/commit/a33ae450cf2c883228c76d04df8df75839c12753))
* remove fading ([d96461d](https://github.com/remvze/moodist/commit/d96461d1ea83c72bfe651d84cf34fabc029c200e))
* resume audio ([8e4d053](https://github.com/remvze/moodist/commit/8e4d0531e0e9aaf4e52b3b3a8666b74ff0c0222e))
* undo changes ([32da26c](https://github.com/remvze/moodist/commit/32da26ccfc0c5bdbe031e26ea48363ea0d8a7b23))
### 🚚 Chores
* add binaural beats ([f1d212a](https://github.com/remvze/moodist/commit/f1d212abc8b69a614bbdc4a23876e2eab7cbb574))
* add more sounds ([38f6f7d](https://github.com/remvze/moodist/commit/38f6f7dbe6898ed78e51eb3f0c7936f003ddca08))
* add more sounds ([937bf29](https://github.com/remvze/moodist/commit/937bf29d09cbce20ea0b6b0c87879f3a7dd1d497))
* add more sounds ([e2172fd](https://github.com/remvze/moodist/commit/e2172fd2bbd0e12a705c9efc98c72ad99d86d006))
* add more sounds ([1f12afa](https://github.com/remvze/moodist/commit/1f12afa3943154d70145ef6adc6aeee79f7a7af3))
* add more sounds ([cd05704](https://github.com/remvze/moodist/commit/cd05704a73ffb33aa0ccf5d789328a4cefc320f1))
* add more sounds ([01b4bdb](https://github.com/remvze/moodist/commit/01b4bdbb572285984bcdc9bb94c1a1b6dd2630c5))
## [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)
### ♻️ Code Refactoring
* change ordering config ([a43c679](https://github.com/remvze/moodist/commit/a43c679e214b24c7f547e182aea6e2fbf826228f))
### 🐛 Bug Fixes
* change icon path ([28c3c40](https://github.com/remvze/moodist/commit/28c3c404ad790869b13731e4c3622abe33f1dda2))
### 🚚 Chores
* add more sounds ([e3864be](https://github.com/remvze/moodist/commit/e3864bede129c102ef5b7258b4688d9177dd284c))
* add more sounds ([55e7f05](https://github.com/remvze/moodist/commit/55e7f05892f6d3200b56a7e06b371bed4b4c4554))
* add more sounds ([318e87c](https://github.com/remvze/moodist/commit/318e87c9f1f3e2509c2b8eeb3a7f6875dd1c02fd))
* add places category ([5970012](https://github.com/remvze/moodist/commit/5970012fa6cbd8222c2be8ce426065f928d81b2b))
* add transport category ([c1c3945](https://github.com/remvze/moodist/commit/c1c3945d43e84e3011de52bffa5116d58283c473))
* change heartbeat audio ([f43a378](https://github.com/remvze/moodist/commit/f43a378697437f671c0c33122b1c9ec5a1e173ff))
* relocate underwater audio ([37bad81](https://github.com/remvze/moodist/commit/37bad8149e1f5170426dc745322c0e65cb9a41ff))
* remove heartbeat audio ([121a8f2](https://github.com/remvze/moodist/commit/121a8f204c6b61490a7ab0e732779031278e6e8c))
### 💄 Styling
* add hover states ([2c74dd0](https://github.com/remvze/moodist/commit/2c74dd0d604af86f99edcba2eb573641ac2010fd))
* change button style ([8efb1ce](https://github.com/remvze/moodist/commit/8efb1cee00ec0e0dcd9677729d9136ca8d69073f))
* change sound counter ([00fc5f3](https://github.com/remvze/moodist/commit/00fc5f3a872be51eb875744e254c75ea58e93281))
* change theme ([bd517f8](https://github.com/remvze/moodist/commit/bd517f88c01202eb7e7e5acf70bf4af2e6f91d75))
## [1.0.0](https://github.com/remvze/moodist/compare/v0.0.1...v1.0.0) (2023-12-09)

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

View File

@@ -2,5 +2,5 @@
<img src="/assets/banner.svg" alt="Moodist Logo Banner" />
<h2>Moodist 🌲</h2>
<p>Ambient sounds for focus and calm.</p>
<a href="https://moodist.app">Visit <strong>Moodist</strong></a>
<a href="https://moodist.app">Visit <strong>Moodist</strong></a> | <a href="https://buymeacoffee.com/remvze">Buy Me a Coffee</a>
</div>

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;
}
}
}

65
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "moodist",
"version": "1.0.0",
"version": "1.3.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "moodist",
"version": "1.0.0",
"version": "1.3.1",
"dependencies": {
"@astrojs/react": "^3.0.3",
"@floating-ui/react": "0.26.0",
@@ -54,7 +54,7 @@
"standard-version": "9.5.0",
"stylelint": "15.10.3",
"stylelint-config-html": "1.1.0",
"stylelint-config-idiomatic-order": "9.0.0",
"stylelint-config-recess-order": "4.4.0",
"stylelint-config-standard": "34.0.0",
"stylelint-prettier": "4.0.2"
}
@@ -12590,15 +12590,6 @@
"node": ">=4"
}
},
"node_modules/postcss-sorting": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-7.0.1.tgz",
"integrity": "sha512-iLBFYz6VRYyLJEJsBJ8M3TCqNcckVzz4wFounSc5Oez35ogE/X+aoC5fFu103Ot7NyvjU3/xqIXn93Gp3kJk4g==",
"dev": true,
"peerDependencies": {
"postcss": "^8.3.9"
}
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
@@ -14922,32 +14913,16 @@
"stylelint": ">=14.0.0"
}
},
"node_modules/stylelint-config-idiomatic-order": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/stylelint-config-idiomatic-order/-/stylelint-config-idiomatic-order-9.0.0.tgz",
"integrity": "sha512-+LtfPycY1Paayf1MaERyh6BzVPnZxemX5NtzdUPqi4u8hyAR7859f/4EL02+Kr9va76iX7mbYC4HendocXKJZQ==",
"node_modules/stylelint-config-recess-order": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-4.4.0.tgz",
"integrity": "sha512-Q99kvZyIM/aoPEV4dRDkzD3fZLzH0LXi+pawCf1r700uUeF/PHQ5PZXjwFUuGrWhOzd1N+cuVm+OUGsY2fRN5A==",
"dev": true,
"dependencies": {
"stylelint-order": "^5.0.0"
},
"engines": {
"node": ">=12"
"stylelint-order": "6.x"
},
"peerDependencies": {
"stylelint": ">=11"
}
},
"node_modules/stylelint-config-idiomatic-order/node_modules/stylelint-order": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-5.0.0.tgz",
"integrity": "sha512-OWQ7pmicXufDw5BlRqzdz3fkGKJPgLyDwD1rFY3AIEfIH/LQY38Vu/85v8/up0I+VPiuGRwbc2Hg3zLAsJaiyw==",
"dev": true,
"dependencies": {
"postcss": "^8.3.11",
"postcss-sorting": "^7.0.1"
},
"peerDependencies": {
"stylelint": "^14.0.0"
"stylelint": ">=15"
}
},
"node_modules/stylelint-config-recommended": {
@@ -14977,6 +14952,28 @@
"stylelint": "^15.10.0"
}
},
"node_modules/stylelint-order": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.4.tgz",
"integrity": "sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==",
"dev": true,
"dependencies": {
"postcss": "^8.4.32",
"postcss-sorting": "^8.0.2"
},
"peerDependencies": {
"stylelint": "^14.0.0 || ^15.0.0 || ^16.0.1"
}
},
"node_modules/stylelint-order/node_modules/postcss-sorting": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.2.tgz",
"integrity": "sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==",
"dev": true,
"peerDependencies": {
"postcss": "^8.4.20"
}
},
"node_modules/stylelint-prettier": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/stylelint-prettier/-/stylelint-prettier-4.0.2.tgz",

View File

@@ -1,7 +1,7 @@
{
"name": "moodist",
"type": "module",
"version": "1.0.0",
"version": "1.3.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
@@ -67,7 +67,7 @@
"standard-version": "9.5.0",
"stylelint": "15.10.3",
"stylelint-config-html": "1.1.0",
"stylelint-config-idiomatic-order": "9.0.0",
"stylelint-config-recess-order": "4.4.0",
"stylelint-config-standard": "34.0.0",
"stylelint-prettier": "4.0.2"
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,7 @@
import { useMemo } from 'react';
import { useMemo, useEffect } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { BiSolidHeart } from 'react-icons/bi/index';
import { Howler } from 'howler';
import { useSoundStore } from '@/store';
@@ -9,7 +10,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';
@@ -35,6 +37,22 @@ export function App() {
);
}, [favorites, categories]);
useEffect(() => {
const onChange = () => {
const { ctx } = Howler;
if (ctx && !document.hidden) {
setTimeout(() => {
ctx.resume();
}, 100);
}
};
document.addEventListener('visibilitychange', onChange, false);
return () => document.removeEventListener('visibilitychange', onChange);
}, []);
const allCategories = useMemo(() => {
const favorites = [];
@@ -60,7 +78,8 @@ export function App() {
</Container>
<ScrollToTop />
<Shuffle />
<Menu />
<SharedModal />
</StoreConsumer>
</SnackbarProvider>
);

View File

@@ -1,11 +1,11 @@
.buttons {
position: sticky;
z-index: 10;
top: 30px;
z-index: 10;
display: flex;
width: max-content;
column-gap: 10px;
align-items: center;
justify-content: center;
width: max-content;
margin: 0 auto;
column-gap: 10px;
}

View File

@@ -1,20 +1,23 @@
.playButton {
display: flex;
width: 150px;
height: 45px;
align-items: center;
justify-content: center;
border: none;
border-radius: 100px;
border-top: 2px solid #34d399;
border-bottom: 3px solid #059669;
background-color: #10b981;
color: #022c22;
cursor: pointer;
width: 150px;
height: 45px;
font-family: var(--font-heading);
font-size: var(--font-base);
line-height: 0;
color: var(--color-neutral-200);
cursor: pointer;
background-color: var(--color-neutral-950);
border: 1px solid var(--color-neutral-50);
border-radius: 100px;
outline: none;
transition: 0.2s;
&:hover {
background-color: var(--color-neutral-800);
}
&:disabled,
&.disabled {

View File

@@ -1,19 +1,17 @@
.unselectButton {
display: flex;
width: 45px;
height: 45px;
align-items: center;
justify-content: center;
border: none;
border-radius: 100px;
border-top: 2px solid #fb7185;
border-bottom: 3px solid #be123c;
background-color: #f43f5e;
color: var(--color-foreground);
cursor: pointer;
width: 45px;
height: 45px;
font-family: var(--font-heading);
font-size: var(--font-md);
line-height: 0;
color: var(--color-foreground);
cursor: pointer;
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300);
border-radius: 100px;
outline: none;
transition: 0.2s;
@@ -22,18 +20,15 @@
cursor: not-allowed;
}
&.restore {
border-top-color: var(--color-neutral-950);
border-bottom-color: var(--color-neutral-600);
background-color: var(--color-neutral-700);
color: var(--color-neutral-200);
&:hover {
background-color: var(--color-neutral-200);
}
}
.tooltip {
padding: 6px 12px;
font-size: var(--font-xsm);
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-200);
border-radius: 100px;
background-color: var(--color-neutral-100);
font-size: var(--font-xsm);
}

View File

@@ -48,7 +48,6 @@ export function UnselectButton() {
}
className={cn(
styles.unselectButton,
hasHistory && styles.restore,
noSelected && !hasHistory && styles.disabled,
)}
onClick={() => {

View File

@@ -1,6 +1,7 @@
import { AnimatePresence } from 'framer-motion';
import { Category } from '@/components/category';
import { Donate } from './donate';
import type { Categories } from '@/data/types';
@@ -11,12 +12,16 @@ interface CategoriesProps {
export function Categories({ categories }: CategoriesProps) {
return (
<AnimatePresence initial={false}>
{categories.map(category => (
<Category
functional={category.id !== 'favorites'}
{...category}
key={category.id}
/>
{categories.map((category, index) => (
<>
<Category
functional={category.id !== 'favorites'}
{...category}
key={category.id}
/>
{index === 3 && <Donate />}
</>
))}
</AnimatePresence>
);

View File

@@ -0,0 +1,63 @@
.donate {
& .iconContainer {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 15px;
& .tail {
width: 1px;
height: 75px;
background: linear-gradient(transparent, var(--color-neutral-300));
}
& .icon {
display: flex;
align-items: center;
justify-content: center;
width: 45px;
height: 45px;
font-size: var(--font-md);
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300);
border-radius: 50%;
}
}
& .title {
font-family: var(--font-display);
font-size: var(--font-lg);
font-weight: 600;
text-align: center;
}
& .desc {
margin-top: 8px;
color: var(--color-foreground-subtle);
text-align: center;
}
.button {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: max-content;
height: 40px;
padding: 0 20px;
margin: 16px auto 0;
font-size: var(--font-xsm);
font-weight: 500;
color: var(--color-neutral-subtle);
text-decoration: none;
cursor: pointer;
background-color: var(--color-neutral-50);
border: 1px solid var(--color-neutral-200);
border-radius: 50px;
transition: 0.2s;
&:hover {
background-color: var(--color-neutral-100);
}
}
}

View File

@@ -0,0 +1,27 @@
import { FaCoffee } from 'react-icons/fa/index';
import styles from './donate.module.css';
export function Donate() {
return (
<div className={styles.donate}>
<div className={styles.iconContainer}>
<div className={styles.tail} />
<div className={styles.icon}>
<FaCoffee />
</div>
</div>
<h2 className={styles.title}>Support Me</h2>
<p className={styles.desc}>Help me keep Moodist ad-free.</p>
<a
className={styles.button}
href="https://buymeacoffee.com/remvze"
rel="noreferrer"
target="_blank"
>
Donate Today
</a>
</div>
);
}

View File

@@ -0,0 +1 @@
export { Donate } from './donate';

View File

@@ -17,14 +17,14 @@
& .icon {
display: flex;
width: 45px;
height: 45px;
align-items: center;
justify-content: center;
width: 45px;
height: 45px;
font-size: var(--font-md);
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300);
border-radius: 50%;
background-color: var(--color-neutral-100);
font-size: var(--font-md);
}
}

View File

@@ -0,0 +1,56 @@
---
import { Container } from '@/components/container';
---
<Container>
<section class="wrapper">
<p class="text">
Enjoy Moodist? <a
href="https://buymeacoffee.com/remvze"
rel="noreferrer"
target="_blank"
>
Support with a donation!
</a>
</p>
</section>
</Container>
<style>
.wrapper {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 16px;
font-size: var(--font-xsm);
color: var(--color-foreground-subtle);
&::after {
position: absolute;
bottom: 0;
left: 50%;
width: 80%;
height: 1px;
content: '';
background: linear-gradient(
90deg,
transparent,
var(--color-neutral-200),
transparent
);
transform: translateX(-50%);
}
& .text {
text-align: center;
& a {
font-weight: 500;
color: var(--color-foreground);
text-decoration: none;
}
}
}
</style>

View File

@@ -16,17 +16,17 @@ import { Container } from '@/components/container';
<style>
.footer {
display: flex;
height: 100px;
align-items: center;
height: 100px;
& p {
color: var(--color-foreground-subtle);
font-size: var(--font-sm);
color: var(--color-foreground-subtle);
text-align: center;
& a {
color: var(--color-foreground);
font-weight: 500;
color: var(--color-foreground);
text-decoration: none;
& span {

View File

@@ -1,5 +1,6 @@
---
import { Balancer } from 'react-wrap-balancer';
import { BsSoundwave } from 'react-icons/bs/index';
import { Container } from '@/components/container';
import { count as soundCount } from '@/lib/sounds';
@@ -27,13 +28,16 @@ const count = soundCount();
<Balancer>Ambient sounds for focus and calm.</Balancer>
</p>
<p class="sounds">{count} Sounds</p>
<p class="sounds">
<span class="icon"><BsSoundwave /></span>
<span>{count} Sounds</span>
</p>
</Container>
</div>
<style>
.hero {
padding: 140px 0 60px;
padding: 100px 0 60px;
text-align: center;
& .logo {
@@ -44,12 +48,12 @@ const count = soundCount();
& .title {
display: flex;
align-items: center;
column-gap: 15px;
align-items: center;
& div {
height: 1px;
flex-grow: 1;
height: 1px;
&.left {
background: linear-gradient(
@@ -77,22 +81,35 @@ const count = soundCount();
& .desc {
margin-top: 5px;
color: var(--color-foreground-subtle);
line-height: 1.6;
color: var(--color-foreground-subtle);
}
& .sounds {
position: relative;
display: flex;
width: max-content;
column-gap: 8px;
align-items: center;
justify-content: center;
padding: 6px 16px;
width: max-content;
height: 28px;
padding-right: 12px;
margin: 20px auto 0;
font-size: var(--font-xsm);
color: var(--color-foreground-subtle);
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-200);
border-radius: 100px;
margin: 20px auto 0;
background-color: var(--color-neutral-100);
font-size: var(--font-xsm);
& .icon {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 0 8px 0 12px;
color: var(--color-foreground);
border-right: 1px solid var(--color-neutral-200);
}
&::before {
position: absolute;
@@ -100,13 +117,13 @@ const count = soundCount();
left: 50%;
width: 70%;
height: 1px;
content: '';
background: linear-gradient(
90deg,
transparent,
var(--color-neutral-400),
transparent
);
content: '';
transform: translateX(-50%);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,34 @@
.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;
text-decoration: none;
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,30 @@
import styles from './item.module.css';
interface ItemProps {
disabled?: boolean;
href?: string;
icon: React.ReactElement;
label: string;
onClick?: () => void;
}
export function Item({
disabled = false,
href,
icon,
label,
onClick = () => {},
}: ItemProps) {
const Comp = href ? 'a' : 'button';
return (
<Comp
className={styles.item}
disabled={disabled}
onClick={onClick}
{...(href ? { href, target: '_blank' } : {})}
>
<span className={styles.icon}>{icon}</span> {label}
</Comp>
);
}

View File

@@ -0,0 +1,13 @@
import { SiBuymeacoffee } from 'react-icons/si/index';
import { Item } from '../item';
export function Donate() {
return (
<Item
href="https://buymeacoffee.com/remvze"
icon={<SiBuymeacoffee />}
label="Buy Me a Coffee"
/>
);
}

View File

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

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,93 @@
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, DonateItem } 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 />
<DonateItem />
</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(2),
}))
.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

@@ -1,17 +1,22 @@
.button {
position: fixed;
z-index: 99;
bottom: 20px;
left: 20px;
z-index: 99;
display: flex;
width: 45px;
height: 45px;
align-items: center;
justify-content: center;
border: 1px solid var(--color-neutral-300);
border-radius: 50%;
background-color: var(--color-neutral-100);
width: 45px;
height: 45px;
font-size: var(--font-md);
color: var(--color-foreground);
cursor: pointer;
font-size: var(--font-md);
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);
}
}

View File

@@ -28,8 +28,8 @@ const count = soundCount();
& .titleWrapper {
display: flex;
align-items: center;
column-gap: 12px;
align-items: center;
& .title {
margin-bottom: 12px;
@@ -39,8 +39,8 @@ const count = soundCount();
}
& .line {
height: 1px;
flex-grow: 1;
height: 1px;
background: linear-gradient(
90deg,
var(--color-neutral-300),
@@ -51,8 +51,8 @@ const count = soundCount();
}
& .desc {
color: var(--color-foreground-subtle);
line-height: 1.7;
color: var(--color-foreground-subtle);
& span {
color: var(--color-foreground);

View File

@@ -36,8 +36,8 @@ import { Container } from '@/components/container';
& .wrapper {
position: relative;
padding: 0 20px 40px;
border-radius: 0 0 20px 20px;
background: linear-gradient(transparent, var(--color-neutral-100));
border-radius: 0 0 20px 20px;
&::after {
position: absolute;
@@ -45,13 +45,13 @@ import { Container } from '@/components/container';
left: 50%;
width: 70%;
height: 1px;
content: '';
background: linear-gradient(
90deg,
transparent,
var(--color-neutral-300),
transparent
);
content: '';
transform: translateX(-50%);
}
}
@@ -70,15 +70,14 @@ import { Container } from '@/components/container';
& .icon {
display: flex;
width: 45px;
height: 45px;
align-items: center;
justify-content: center;
width: 45px;
height: 45px;
font-size: var(--font-md);
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300);
border-radius: 50%;
background-color: var(--color-neutral-100);
color: #fbbf24;
font-size: var(--font-md);
}
}
@@ -97,23 +96,26 @@ import { Container } from '@/components/container';
& .button {
display: flex;
width: 120px;
height: 40px;
align-items: center;
justify-content: center;
border: none;
border-radius: 100px;
border-top: 2px solid var(--color-neutral-950);
border-bottom: 3px solid var(--color-neutral-600);
width: 120px;
height: 40px;
margin: 24px auto 0;
background-color: var(--color-neutral-700);
color: var(--color-neutral-200);
cursor: pointer;
font-family: var(--font-heading);
font-size: var(--font-sm);
line-height: 0;
outline: none;
color: var(--color-neutral-200);
text-decoration: none;
cursor: pointer;
background-color: var(--color-neutral-950);
border: none;
border-radius: 100px;
outline: none;
transition: 0.2s;
&:hover {
background-color: var(--color-neutral-800);
}
}
}
</style>

View File

@@ -90,8 +90,8 @@ const reasons = [
& .titleWrapper {
display: flex;
align-items: center;
column-gap: 12px;
align-items: center;
& .title {
margin-bottom: 12px;
@@ -101,8 +101,8 @@ const reasons = [
}
& .line {
height: 1px;
flex-grow: 1;
height: 1px;
background: linear-gradient(
90deg,
var(--color-neutral-300),
@@ -114,23 +114,23 @@ const reasons = [
& .reasons {
display: grid;
margin-top: 24px;
column-gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
row-gap: 28px;
column-gap: 20px;
margin-top: 24px;
& .icon {
display: flex;
width: 40px;
height: 40px;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
margin-bottom: 12px;
font-size: var(--font-md);
color: var(--color-foreground-subtle);
background: linear-gradient(var(--color-neutral-100), transparent);
border: 1px solid var(--color-neutral-200);
border-radius: 12px;
margin-bottom: 12px;
background: linear-gradient(var(--color-neutral-100), transparent);
color: #34d399;
font-size: var(--font-md);
}
& .label {
@@ -140,16 +140,16 @@ const reasons = [
}
& .body {
color: var(--color-foreground-subtle);
line-height: 1.6;
color: var(--color-foreground-subtle);
}
& .link {
display: block;
margin-top: 8px;
color: var(--color-foreground);
font-size: var(--font-sm);
font-weight: 500;
color: var(--color-foreground);
text-decoration: none;
}
}

View File

@@ -1,17 +1,22 @@
.button {
position: fixed;
z-index: 99;
right: 20px;
bottom: 20px;
z-index: 99;
display: flex;
width: 45px;
height: 45px;
align-items: center;
justify-content: center;
border: 1px solid var(--color-neutral-300);
border-radius: 50%;
background-color: var(--color-neutral-100);
width: 45px;
height: 45px;
font-size: var(--font-md);
color: var(--color-foreground);
cursor: pointer;
font-size: var(--font-md);
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);
}
}

View File

@@ -1,8 +1,8 @@
.wrapper {
position: fixed;
z-index: 100;
bottom: 20px;
left: 0;
z-index: 100;
width: 100%;
pointer-events: none;
@@ -10,11 +10,11 @@
width: max-content;
max-width: 90%;
padding: 12px 16px;
border: 1px solid var(--color-neutral-300);
border-radius: 4px;
margin: 0 auto;
background-color: var(--color-neutral-200);
font-size: var(--font-sm);
pointer-events: fill;
background-color: var(--color-neutral-200);
border: 1px solid var(--color-neutral-300);
border-radius: 4px;
}
}

View File

@@ -3,20 +3,25 @@
top: 10px;
right: 10px;
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
border: 1px solid var(--color-neutral-200);
border-radius: 50%;
background-color: black;
background-color: var(--color-neutral-100);
width: 30px;
height: 30px;
line-height: 0;
color: var(--color-foreground-subtle);
cursor: pointer;
line-height: 0;
background-color: black;
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-200);
border-radius: 50%;
outline: none;
transition: 0.2s;
&:hover {
color: var(--color-foreground);
}
&.isFavorite {
color: #f43f5e;
color: var(--color-foreground);
}
}

View File

@@ -7,8 +7,8 @@
/* Range Reset */
appearance: none;
background: transparent;
cursor: pointer;
background: transparent;
/* Removes default focus */
&:focus {
@@ -16,27 +16,27 @@
}
&:disabled {
pointer-events: none;
cursor: default;
opacity: 0.5;
pointer-events: none;
}
/***** Chrome, Safari, Opera and Edge Chromium styles *****/
&::-webkit-slider-runnable-track {
height: 0.5rem;
border-radius: 0.5rem;
background-color: #27272a;
border-radius: 0.5rem;
}
&::-webkit-slider-thumb {
width: 14px;
height: 14px;
border: 1px solid #52525b;
border-radius: 50%;
margin-top: -3px;
appearance: none;
background-color: #3f3f46;
border: 1px solid #52525b;
border-radius: 50%;
}
&:not(:disabled):focus::-webkit-slider-thumb {
@@ -49,19 +49,19 @@
&::-moz-range-track {
height: 0.5rem;
border-radius: 0.5rem;
background-color: #27272a;
border-radius: 0.5rem;
}
&::-moz-range-thumb {
width: 14px;
height: 14px;
margin-top: -3px;
background-color: #3f3f46;
border: none;
border: 1px solid #52525b;
border-radius: 0;
border-radius: 50%;
margin-top: -3px;
background-color: #3f3f46;
}
&:not(:disabled):focus::-moz-range-thumb {

View File

@@ -5,10 +5,10 @@
align-items: center;
justify-content: center;
padding: 25px 20px;
text-align: center;
background: linear-gradient(var(--color-neutral-100), transparent);
border: 1px solid var(--color-neutral-200);
border-radius: 8px;
background: linear-gradient(var(--color-neutral-100), transparent);
text-align: center;
transition: 0.2s;
&.hidden {
@@ -21,80 +21,83 @@
left: 0;
width: 100%;
height: 1px;
content: '';
background: linear-gradient(
90deg,
transparent,
var(--color-neutral-400),
transparent
);
content: '';
}
& .icon {
position: relative;
z-index: 2;
display: flex;
width: 40px;
height: 40px;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
font-size: var(--font-base);
color: var(--color-foreground-subtler);
transition: 0.2s;
&::after {
position: absolute;
z-index: -1;
top: 0;
left: 0;
z-index: -1;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: var(--color-neutral-50);
content: '';
background-color: var(--color-neutral-50);
border-radius: 50%;
}
&::before {
position: absolute;
z-index: -2;
top: -1px;
left: -1px;
z-index: -2;
width: calc(100% + 2px);
height: calc(100% + 2px);
border-radius: 50%;
content: '';
background: linear-gradient(
var(--color-neutral-300),
var(--color-neutral-100)
);
content: '';
border-radius: 50%;
}
& .spinner {
animation-duration: 1s;
animation-iteration-count: infinite;
animation-name: spinner;
animation-timing-function: linear;
line-height: 0;
animation-name: spinner;
animation-duration: 1s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
}
&:hover .icon {
color: var(--color-foreground-subtle);
}
&.selected {
border-color: transparent;
box-shadow:
0 0 0 2px #34d399,
0 10px 20px #34d39933;
box-shadow: 0 0 0 2px var(--color-neutral-800);
& .icon {
color: #34d399;
color: var(--color-foreground);
}
}
& h3 {
margin-top: 8px;
cursor: default;
font-family: var(--font-heading);
font-size: var(--font-sm);
font-weight: 600;
line-height: 1.6;
cursor: default;
}
}

View File

@@ -5,7 +5,7 @@ import { Range } from './range';
import { Favorite } from './favorite';
import { useSound } from '@/hooks/use-sound';
import { useSoundStore } from '@/store';
import { useSoundStore, useLoadingStore } from '@/store';
import { cn } from '@/helpers/styles';
import styles from './sound.module.css';
@@ -37,6 +37,8 @@ export function Sound({
const volume = useSoundStore(state => state.sounds[id].volume);
const isSelected = useSoundStore(state => state.sounds[id].isSelected);
const isLoading = useLoadingStore(state => state.loaders[src]);
const sound = useSound(src, { loop: true, volume });
useEffect(() => {
@@ -80,7 +82,7 @@ export function Sound({
>
<Favorite id={id} />
<div className={styles.icon}>
{sound.isLoading ? (
{isLoading ? (
<span className={styles.spinner}>
<ImSpinner9 />
</span>

View File

@@ -1,25 +1,25 @@
.sounds {
display: grid;
margin-top: 20px;
gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin-top: 20px;
}
.button {
position: relative;
display: flex;
width: max-content;
height: 35px;
align-items: center;
justify-content: center;
width: max-content;
height: 35px;
padding: 0 15px;
border: 1px solid var(--color-neutral-200);
border-radius: 50px;
margin: 25px auto 0;
background-color: var(--color-neutral-50);
font-size: var(--font-xsm);
color: var(--color-neutral-subtle);
cursor: pointer;
font-size: var(--font-xsm);
background-color: var(--color-neutral-50);
border: 1px solid var(--color-neutral-200);
border-radius: 50px;
&::before {
position: absolute;
@@ -27,13 +27,13 @@
left: 50%;
width: 70%;
height: 1px;
content: '';
background: linear-gradient(
90deg,
transparent,
var(--color-neutral-300),
transparent
);
content: '';
transform: translateX(-50%);
}
@@ -43,8 +43,8 @@
right: 0;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #34d399;
content: '';
background-color: var(--color-neutral-950);
border-radius: 50%;
}
}

View File

@@ -1,8 +1,8 @@
.tooltip {
width: max-content;
padding: 6px 12px;
font-size: var(--font-xsm);
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-200);
border-radius: 100px;
background-color: var(--color-neutral-100);
font-size: var(--font-xsm);
}

View File

@@ -2,13 +2,26 @@ import { nature } from './sounds/nature';
import { rain } from './sounds/rain';
import { animals } from './sounds/animals';
import { urban } from './sounds/urban';
import { places } from './sounds/places';
import { transport } from './sounds/transport';
import { things } from './sounds/things';
import { noise } from './sounds/noise';
import { binaural } from './sounds/binaural';
import type { Categories } from './types';
export const sounds: {
categories: Categories;
} = {
categories: [nature, rain, animals, urban, things, noise],
categories: [
nature,
rain,
animals,
urban,
places,
transport,
things,
noise,
binaural,
],
};

Some files were not shown because too many files have changed in this diff Show More