diff --git a/CHANGELOG.md b/CHANGELOG.md index 39a57ec3c..8d1b186b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### App +- General + - Added a light theme ! - Performance - Improved app's reactivity - Added some chunk splitting for a faster initial load @@ -13,10 +15,18 @@ - Chart - Fixed legend hovering on mobile not resetting on touch end - Updated legend padding so that the scrollbar, if visible, is less in the way - - Added yearly time scale setters (from year 2009 to today) + - Added "3 months" and yearly time scale setters (from year 2009 to today) + - Hide scrollbar of timescale setters + - Changed scroll buttons visibility by screen type (touchscreen or not) instead of screen size + - Added scroll buttons to the legend + - Tweaked scroll buttons background and gradient color from black to stone gray + - Improved Share/QR Code screen +- Settings + - Finally made a proper component where you can chose the app's theme, between a moving or static background and its text opacity - Misc - Support mini window size, could be useful for embedded views - Hopefully made scrollbars a little more subtle on WIndows and Linux, can't test + - Generale style updates ## v. 0.1.1 | 849240 - 2024/06/24 diff --git a/app/index.html b/app/index.html index 7a1d67275..ab527b4ee 100644 --- a/app/index.html +++ b/app/index.html @@ -1,5 +1,5 @@ - + Satonomics @@ -362,7 +362,10 @@ media="(prefers-color-scheme: dark) and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" /> - +
diff --git a/app/package.json b/app/package.json index 4c8989247..f24d5dad5 100644 --- a/app/package.json +++ b/app/package.json @@ -23,14 +23,14 @@ "@solid-primitives/resize-observer": "^2.0.25", "lean-qr": "^2.3.4", "lightweight-charts": "^4.1.6", - "solid-js": "^1.8.17" + "solid-js": "^1.8.18" }, "devDependencies": { - "@ianvs/prettier-plugin-sort-imports": "^4.2.1", - "@iconify-json/tabler": "^1.1.114", + "@ianvs/prettier-plugin-sort-imports": "^4.3.0", + "@iconify-json/tabler": "^1.1.115", "@tailwindcss/container-queries": "^0.1.1", "autoprefixer": "^10.4.19", - "postcss": "^8.4.38", + "postcss": "^8.4.39", "prettier": "^3.3.2", "prettier-plugin-tailwindcss": "^0.6.5", "pwa-asset-generator": "^6.3.1", @@ -39,10 +39,10 @@ "typescript": "^5.5.2", "unplugin-auto-import": "^0.17.6", "unplugin-icons": "^0.19.0", - "vite": "^5.3.1", + "vite": "^5.3.2", "vite-plugin-pwa": "^0.20.0", "vite-plugin-solid": "^2.10.2", "workbox-window": "^7.1.0", - "wrangler": "^3.61.0" + "wrangler": "^3.62.0" } } diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index ef89ea459..d64b6f197 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -10,16 +10,16 @@ dependencies: version: 1.0.14 '@solid-primitives/event-listener': specifier: ^2.3.3 - version: 2.3.3(solid-js@1.8.17) + version: 2.3.3(solid-js@1.8.18) '@solid-primitives/intersection-observer': specifier: ^2.1.6 - version: 2.1.6(solid-js@1.8.17) + version: 2.1.6(solid-js@1.8.18) '@solid-primitives/memo': specifier: ^1.3.8 - version: 1.3.8(solid-js@1.8.17) + version: 1.3.8(solid-js@1.8.18) '@solid-primitives/resize-observer': specifier: ^2.0.25 - version: 2.0.25(solid-js@1.8.17) + version: 2.0.25(solid-js@1.8.18) lean-qr: specifier: ^2.3.4 version: 2.3.4 @@ -27,31 +27,31 @@ dependencies: specifier: ^4.1.6 version: 4.1.6 solid-js: - specifier: ^1.8.17 - version: 1.8.17 + specifier: ^1.8.18 + version: 1.8.18 devDependencies: '@ianvs/prettier-plugin-sort-imports': - specifier: ^4.2.1 - version: 4.2.1(prettier@3.3.2) + specifier: ^4.3.0 + version: 4.3.0(prettier@3.3.2) '@iconify-json/tabler': - specifier: ^1.1.114 - version: 1.1.114 + specifier: ^1.1.115 + version: 1.1.115 '@tailwindcss/container-queries': specifier: ^0.1.1 version: 0.1.1(tailwindcss@3.4.4) autoprefixer: specifier: ^10.4.19 - version: 10.4.19(postcss@8.4.38) + version: 10.4.19(postcss@8.4.39) postcss: - specifier: ^8.4.38 - version: 8.4.38 + specifier: ^8.4.39 + version: 8.4.39 prettier: specifier: ^3.3.2 version: 3.3.2 prettier-plugin-tailwindcss: specifier: ^0.6.5 - version: 0.6.5(@ianvs/prettier-plugin-sort-imports@4.2.1)(prettier@3.3.2) + version: 0.6.5(@ianvs/prettier-plugin-sort-imports@4.3.0)(prettier@3.3.2) pwa-asset-generator: specifier: ^6.3.1 version: 6.3.1 @@ -71,20 +71,20 @@ devDependencies: specifier: ^0.19.0 version: 0.19.0 vite: - specifier: ^5.3.1 - version: 5.3.1 + specifier: ^5.3.2 + version: 5.3.2 vite-plugin-pwa: specifier: ^0.20.0 - version: 0.20.0(vite@5.3.1)(workbox-build@7.1.1)(workbox-window@7.1.0) + version: 0.20.0(vite@5.3.2)(workbox-build@7.1.1)(workbox-window@7.1.0) vite-plugin-solid: specifier: ^2.10.2 - version: 2.10.2(solid-js@1.8.17)(vite@5.3.1) + version: 2.10.2(solid-js@1.8.18)(vite@5.3.2) workbox-window: specifier: ^7.1.0 version: 7.1.0 wrangler: - specifier: ^3.61.0 - version: 3.61.0 + specifier: ^3.62.0 + version: 3.62.0 packages: @@ -114,8 +114,8 @@ packages: '@jsdevtools/ez-spawn': 3.0.4 dev: true - /@antfu/utils@0.7.8: - resolution: {integrity: sha512-rWQkqXRESdjXtc+7NRfK9lASQjpXJu1ayp7qi1d23zZorY+wBHVLHHoVcMsEnkqEBWTFqbztO7/QdJFzyEcLTg==} + /@antfu/utils@0.7.10: + resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} dev: true /@apideck/better-ajv-errors@0.3.6(ajv@8.16.0): @@ -1383,15 +1383,15 @@ packages: to-fast-properties: 2.0.0 dev: true - /@cloudflare/kv-asset-handler@0.3.3: - resolution: {integrity: sha512-wpE+WiWW2kUNwNE0xyl4CtTAs+STjGtouHGiZPGRaisGB7eXXdbvfZdOrQJQVKgTxZiNAgVgmc7fj0sUmd8zyA==} + /@cloudflare/kv-asset-handler@0.3.4: + resolution: {integrity: sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==} engines: {node: '>=16.13'} dependencies: mime: 3.0.0 dev: true - /@cloudflare/workerd-darwin-64@1.20240610.1: - resolution: {integrity: sha512-YanZ1iXgMGaUWlleB5cswSE6qbzyjQ8O7ENWZcPAcZZ6BfuL7q3CWi0t9iM1cv2qx92rRztsRTyjcfq099++XQ==} + /@cloudflare/workerd-darwin-64@1.20240620.1: + resolution: {integrity: sha512-YWeS2aE8jAzDefuus/3GmZcFGu3Ef94uCAoxsQuaEXNsiGM9NeAhPpKC1BJAlcv168U/Q1J+6hckcGtipf6ZcQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] @@ -1399,8 +1399,8 @@ packages: dev: true optional: true - /@cloudflare/workerd-darwin-arm64@1.20240610.1: - resolution: {integrity: sha512-bRe/y/LKjIgp3L2EHjc+CvoCzfHhf4aFTtOBkv2zW+VToNJ4KlXridndf7LvR9urfsFRRo9r4TXCssuKaU+ypQ==} + /@cloudflare/workerd-darwin-arm64@1.20240620.1: + resolution: {integrity: sha512-3rdND+EHpmCrwYX6hvxIBSBJ0f40tRNxond1Vfw7GiR1MJVi3gragiBx75UDFHCxfRw3J0GZ1qVlkRce2/Xbsg==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] @@ -1408,8 +1408,8 @@ packages: dev: true optional: true - /@cloudflare/workerd-linux-64@1.20240610.1: - resolution: {integrity: sha512-2zDcadR7+Gs9SjcMXmwsMji2Xs+yASGNA2cEHDuFc4NMUup+eL1mkzxc/QzvFjyBck98e92rBjMZt2dVscpGKg==} + /@cloudflare/workerd-linux-64@1.20240620.1: + resolution: {integrity: sha512-tURcTrXGeSbYqeM5ISVcofY20StKbVIcdxjJvNYNZ+qmSV9Fvn+zr7rRE+q64pEloVZfhsEPAlUCnFso5VV4XQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] @@ -1417,8 +1417,8 @@ packages: dev: true optional: true - /@cloudflare/workerd-linux-arm64@1.20240610.1: - resolution: {integrity: sha512-7y41rPi5xmIYJN8CY+t3RHnjLL0xx/WYmaTd/j552k1qSr02eTE2o/TGyWZmGUC+lWnwdPQJla0mXbvdqgRdQg==} + /@cloudflare/workerd-linux-arm64@1.20240620.1: + resolution: {integrity: sha512-TThvkwNxaZFKhHZnNjOGqIYCOk05DDWgO+wYMuXg15ymN/KZPnCicRAkuyqiM+R1Fgc4kwe/pehjP8pbmcf6sg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] @@ -1426,8 +1426,8 @@ packages: dev: true optional: true - /@cloudflare/workerd-windows-64@1.20240610.1: - resolution: {integrity: sha512-B0LyT3DB6rXHWNptnntYHPaoJIy0rXnGfeDBM3nEVV8JIsQrx8MEFn2F2jYioH1FkUVavsaqKO/zUosY3tZXVA==} + /@cloudflare/workerd-windows-64@1.20240620.1: + resolution: {integrity: sha512-Y/BA9Yj0r7Al1HK3nDHcfISgFllw6NR3XMMPChev57vrVT9C9D4erBL3sUBfofHU+2U9L+ShLsl6obBpe3vvUw==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -1870,8 +1870,8 @@ packages: engines: {node: '>=14'} dev: true - /@ianvs/prettier-plugin-sort-imports@4.2.1(prettier@3.3.2): - resolution: {integrity: sha512-NKN1LVFWUDGDGr3vt+6Ey3qPeN/163uR1pOPAlkWpgvAqgxQ6kSdUf1F0it8aHUtKRUzEGcK38Wxd07O61d7+Q==} + /@ianvs/prettier-plugin-sort-imports@4.3.0(prettier@3.3.2): + resolution: {integrity: sha512-OOMtUcO4J3LoL63dOKAe7bn+lSRRPeit2DqNHpx+wvBp3Grejo2PMaK4Mp1mwy8pnat64ccSgk/lBZbsAdLErw==} peerDependencies: '@vue/compiler-sfc': 2.7.x || 3.x prettier: 2 || 3 @@ -1890,8 +1890,8 @@ packages: - supports-color dev: true - /@iconify-json/tabler@1.1.114: - resolution: {integrity: sha512-AaTTGEyiPQ7VAYyXGQ9jUI8+8iL6xanucYsACz6f3U6JLph6jDyicXXUh+dYM6HxW6TGehwVqRO2NSIQpACszw==} + /@iconify-json/tabler@1.1.115: + resolution: {integrity: sha512-nyD8OmtQhBl6FLptfVJe04fjoLIUT3sxe4sEChrXhVDuYQlb1DUPEQQkbwjAIzP4w9JcNYwdUpVbIWn60AjECw==} dependencies: '@iconify/types': 2.0.0 dev: true @@ -1904,7 +1904,7 @@ packages: resolution: {integrity: sha512-Y+iGko8uv/Fz5bQLLJyNSZGOdMW0G7cnlEX1CiNcKsRXX9cq/y/vwxrIAtLCZhKHr3m0VJmsjVPsvnM4uX8YLg==} dependencies: '@antfu/install-pkg': 0.1.1 - '@antfu/utils': 0.7.8 + '@antfu/utils': 0.7.10 '@iconify/types': 2.0.0 debug: 4.3.5 kolorist: 1.8.0 @@ -2233,78 +2233,78 @@ packages: dev: true optional: true - /@solid-primitives/event-listener@2.3.3(solid-js@1.8.17): + /@solid-primitives/event-listener@2.3.3(solid-js@1.8.18): resolution: {integrity: sha512-DAJbl+F0wrFW2xmcV8dKMBhk9QLVLuBSW+TR4JmIfTaObxd13PuL7nqaXnaYKDWOYa6otB00qcCUIGbuIhSUgQ==} peerDependencies: solid-js: ^1.6.12 dependencies: - '@solid-primitives/utils': 6.2.3(solid-js@1.8.17) - solid-js: 1.8.17 + '@solid-primitives/utils': 6.2.3(solid-js@1.8.18) + solid-js: 1.8.18 dev: false - /@solid-primitives/intersection-observer@2.1.6(solid-js@1.8.17): + /@solid-primitives/intersection-observer@2.1.6(solid-js@1.8.18): resolution: {integrity: sha512-SeiCmN/R46Z+o9+5HhIQzSor0DqVPyo4ROLQMvCI8AsGZl/5nHlWzHTTbWPeukVUXTgb04wfC3DUo9IzF/XloA==} peerDependencies: solid-js: ^1.6.12 dependencies: - '@solid-primitives/utils': 6.2.3(solid-js@1.8.17) - solid-js: 1.8.17 + '@solid-primitives/utils': 6.2.3(solid-js@1.8.18) + solid-js: 1.8.18 dev: false - /@solid-primitives/memo@1.3.8(solid-js@1.8.17): + /@solid-primitives/memo@1.3.8(solid-js@1.8.18): resolution: {integrity: sha512-U75pfLFSxFmM2xbx1+2XPPyWbaXrnUFF10spbFuOUgJ7azrC+4y+FnrVi4RKqHw9gftd8aKQuTiyMQq468YLQw==} peerDependencies: solid-js: ^1.6.12 dependencies: - '@solid-primitives/scheduled': 1.4.3(solid-js@1.8.17) - '@solid-primitives/utils': 6.2.3(solid-js@1.8.17) - solid-js: 1.8.17 + '@solid-primitives/scheduled': 1.4.3(solid-js@1.8.18) + '@solid-primitives/utils': 6.2.3(solid-js@1.8.18) + solid-js: 1.8.18 dev: false - /@solid-primitives/resize-observer@2.0.25(solid-js@1.8.17): + /@solid-primitives/resize-observer@2.0.25(solid-js@1.8.18): resolution: {integrity: sha512-jVDXkt2MiriYRaz4DYs62185d+6jQ+1DCsR+v7f6XMsIJJuf963qdBRFjtZtKXBaxdPNMyuPeDgf5XQe3EoDJg==} peerDependencies: solid-js: ^1.6.12 dependencies: - '@solid-primitives/event-listener': 2.3.3(solid-js@1.8.17) - '@solid-primitives/rootless': 1.4.5(solid-js@1.8.17) - '@solid-primitives/static-store': 0.0.8(solid-js@1.8.17) - '@solid-primitives/utils': 6.2.3(solid-js@1.8.17) - solid-js: 1.8.17 + '@solid-primitives/event-listener': 2.3.3(solid-js@1.8.18) + '@solid-primitives/rootless': 1.4.5(solid-js@1.8.18) + '@solid-primitives/static-store': 0.0.8(solid-js@1.8.18) + '@solid-primitives/utils': 6.2.3(solid-js@1.8.18) + solid-js: 1.8.18 dev: false - /@solid-primitives/rootless@1.4.5(solid-js@1.8.17): + /@solid-primitives/rootless@1.4.5(solid-js@1.8.18): resolution: {integrity: sha512-GFJE9GC3ojx0aUKqAUZmQPyU8fOVMtnVNrkdk2yS4kd17WqVSpXpoTmo9CnOwA+PG7FTzdIkogvfLQSLs4lrww==} peerDependencies: solid-js: ^1.6.12 dependencies: - '@solid-primitives/utils': 6.2.3(solid-js@1.8.17) - solid-js: 1.8.17 + '@solid-primitives/utils': 6.2.3(solid-js@1.8.18) + solid-js: 1.8.18 dev: false - /@solid-primitives/scheduled@1.4.3(solid-js@1.8.17): + /@solid-primitives/scheduled@1.4.3(solid-js@1.8.18): resolution: {integrity: sha512-HfWN5w7b7FEc6VPLBKnnE302h90jsLMuR28Fcf7neRGGf8jBj6wm6/UFQ00VlKexHFMR6KQ2u4VBh5a1ZcqM8g==} peerDependencies: solid-js: ^1.6.12 dependencies: - solid-js: 1.8.17 + solid-js: 1.8.18 dev: false - /@solid-primitives/static-store@0.0.8(solid-js@1.8.17): + /@solid-primitives/static-store@0.0.8(solid-js@1.8.18): resolution: {integrity: sha512-ZecE4BqY0oBk0YG00nzaAWO5Mjcny8Fc06CdbXadH9T9lzq/9GefqcSe/5AtdXqjvY/DtJ5C6CkcjPZO0o/eqg==} peerDependencies: solid-js: ^1.6.12 dependencies: - '@solid-primitives/utils': 6.2.3(solid-js@1.8.17) - solid-js: 1.8.17 + '@solid-primitives/utils': 6.2.3(solid-js@1.8.18) + solid-js: 1.8.18 dev: false - /@solid-primitives/utils@6.2.3(solid-js@1.8.17): + /@solid-primitives/utils@6.2.3(solid-js@1.8.18): resolution: {integrity: sha512-CqAwKb2T5Vi72+rhebSsqNZ9o67buYRdEJrIFzRXz3U59QqezuuxPsyzTSVCacwS5Pf109VRsgCJQoxKRoECZQ==} peerDependencies: solid-js: ^1.6.12 dependencies: - solid-js: 1.8.17 + solid-js: 1.8.18 dev: false /@surma/rollup-plugin-off-main-thread@2.2.3: @@ -2368,11 +2368,11 @@ packages: /@types/node-forge@1.3.11: resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 dev: true - /@types/node@20.14.8: - resolution: {integrity: sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==} + /@types/node@20.14.9: + resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==} dependencies: undici-types: 5.26.5 dev: true @@ -2393,7 +2393,7 @@ packages: resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} requiresBuild: true dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 dev: true optional: true @@ -2520,7 +2520,7 @@ packages: engines: {node: '>= 4.0.0'} dev: true - /autoprefixer@10.4.19(postcss@8.4.38): + /autoprefixer@10.4.19(postcss@8.4.39): resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -2528,11 +2528,11 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.23.1 - caniuse-lite: 1.0.30001636 + caniuse-lite: 1.0.30001638 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.1 - postcss: 8.4.38 + postcss: 8.4.39 postcss-value-parser: 4.2.0 dev: true @@ -2543,8 +2543,8 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /babel-plugin-jsx-dom-expressions@0.37.21(@babel/core@7.24.7): - resolution: {integrity: sha512-WbQo1NQ241oki8bYasVzkMXOTSIri5GO/K47rYJb2ZBh8GaPUEWiWbMV3KwXz+96eU2i54N6ThzjQG/f5n8Azw==} + /babel-plugin-jsx-dom-expressions@0.37.23(@babel/core@7.24.7): + resolution: {integrity: sha512-Y/r8LyLi/njnwPTaDuPEReWk30FJ1KplloYvcFUhHmiH1F7yVVj5mWojD7mbO/IruKyvOs9OIPUoeMi3Z++J4w==} peerDependencies: '@babel/core': ^7.20.12 dependencies: @@ -2592,13 +2592,13 @@ packages: - supports-color dev: true - /babel-preset-solid@1.8.17(@babel/core@7.24.7): - resolution: {integrity: sha512-s/FfTZOeds0hYxYqce90Jb+0ycN2lrzC7VP1k1JIn3wBqcaexDKdYi6xjB+hMNkL+Q6HobKbwsriqPloasR9LA==} + /babel-preset-solid@1.8.18(@babel/core@7.24.7): + resolution: {integrity: sha512-ky0FA4cCS9dk+xYBBItHoxtbRnaDIOGpmHLFqKPaR81hpMbJBOiLOZia2hT0JBwx4zn/D2OjMRvRr6kqtRMoUw==} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.7 - babel-plugin-jsx-dom-expressions: 0.37.21(@babel/core@7.24.7) + babel-plugin-jsx-dom-expressions: 0.37.23(@babel/core@7.24.7) dev: true /balanced-match@1.0.2: @@ -2655,8 +2655,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001636 - electron-to-chromium: 1.4.810 + caniuse-lite: 1.0.30001638 + electron-to-chromium: 1.4.815 node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.1) dev: true @@ -2715,8 +2715,8 @@ packages: engines: {node: '>=6'} dev: true - /caniuse-lite@1.0.30001636: - resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==} + /caniuse-lite@1.0.30001638: + resolution: {integrity: sha512-5SuJUJ7cZnhPpeLHaH0c/HPAnAHZvS6ElWyHK9GSIbVOQABLzowiI2pjmpvZ1WEbkyz46iFd4UXlOHR5SqgfMQ==} dev: true /capnp-ts@0.7.0: @@ -2793,7 +2793,7 @@ packages: engines: {node: '>=12.13.0'} hasBin: true dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -2976,6 +2976,10 @@ packages: is-data-view: 1.0.1 dev: true + /date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + dev: true + /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -3118,8 +3122,8 @@ packages: jake: 10.9.1 dev: true - /electron-to-chromium@1.4.810: - resolution: {integrity: sha512-Kaxhu4T7SJGpRQx99tq216gCq2nMxJo+uuT6uzz9l8TVN2stL7M06MIIXAtr9jsrLs2Glflgf2vMQRepxawOdQ==} + /electron-to-chromium@1.4.815: + resolution: {integrity: sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==} dev: true /emoji-regex@8.0.0: @@ -3596,7 +3600,7 @@ packages: dependencies: foreground-child: 3.2.1 jackspeak: 3.4.0 - minimatch: 9.0.4 + minimatch: 9.0.5 minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 1.11.1 @@ -4158,8 +4162,8 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true - /lru-cache@10.2.2: - resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} + /lru-cache@10.3.0: + resolution: {integrity: sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==} engines: {node: 14 || >=16.14} dev: true @@ -4272,8 +4276,8 @@ packages: engines: {node: '>=4'} dev: true - /miniflare@3.20240610.1: - resolution: {integrity: sha512-ZkfSpBmX3nJW00yYhvF2kGvjb6f77TOimRR6+2GQvsArbwo6e0iYqLGM9aB/cnJzgFjLMvOv1qj4756iynSxJQ==} + /miniflare@3.20240620.0: + resolution: {integrity: sha512-NBMzqUE2mMlh/hIdt6U5MP+aFhEjKDq3l8CAajXAQa1WkndJdciWvzB2mfLETwoVFhMl/lphaVzyEN2AgwJpbQ==} engines: {node: '>=16.13'} hasBin: true dependencies: @@ -4285,7 +4289,7 @@ packages: glob-to-regexp: 0.4.1 stoppable: 1.1.0 undici: 5.28.4 - workerd: 1.20240610.1 + workerd: 1.20240620.1 ws: 8.17.1 youch: 3.3.3 zod: 3.23.8 @@ -4315,8 +4319,8 @@ packages: brace-expansion: 2.0.1 dev: true - /minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + /minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 @@ -4586,7 +4590,7 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} dependencies: - lru-cache: 10.2.2 + lru-cache: 10.3.0 minipass: 7.1.2 dev: true @@ -4641,29 +4645,29 @@ packages: engines: {node: '>= 0.4'} dev: true - /postcss-import@15.1.0(postcss@8.4.38): + /postcss-import@15.1.0(postcss@8.4.39): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: postcss: ^8.0.0 dependencies: - postcss: 8.4.38 + postcss: 8.4.39 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 dev: true - /postcss-js@4.0.1(postcss@8.4.38): + /postcss-js@4.0.1(postcss@8.4.39): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 dependencies: camelcase-css: 2.0.1 - postcss: 8.4.38 + postcss: 8.4.39 dev: true - /postcss-load-config@4.0.2(postcss@8.4.38): + /postcss-load-config@4.0.2(postcss@8.4.39): resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} engines: {node: '>= 14'} peerDependencies: @@ -4676,17 +4680,17 @@ packages: optional: true dependencies: lilconfig: 3.1.2 - postcss: 8.4.38 + postcss: 8.4.39 yaml: 2.4.5 dev: true - /postcss-nested@6.0.1(postcss@8.4.38): + /postcss-nested@6.0.1(postcss@8.4.39): resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.4.38 + postcss: 8.4.39 postcss-selector-parser: 6.1.0 dev: true @@ -4702,8 +4706,8 @@ packages: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true - /postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + /postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 @@ -4711,7 +4715,7 @@ packages: source-map-js: 1.2.0 dev: true - /prettier-plugin-tailwindcss@0.6.5(@ianvs/prettier-plugin-sort-imports@4.2.1)(prettier@3.3.2): + /prettier-plugin-tailwindcss@0.6.5(@ianvs/prettier-plugin-sort-imports@4.3.0)(prettier@3.3.2): resolution: {integrity: sha512-axfeOArc/RiGHjOIy9HytehlC0ZLeMaqY09mm8YCkMzznKiDkwFzOpBvtuhuv3xG5qB73+Mj7OCe2j/L1ryfuQ==} engines: {node: '>=14.21.3'} peerDependencies: @@ -4763,7 +4767,7 @@ packages: prettier-plugin-svelte: optional: true dependencies: - '@ianvs/prettier-plugin-sort-imports': 4.2.1(prettier@3.3.2) + '@ianvs/prettier-plugin-sort-imports': 4.3.0(prettier@3.3.2) prettier: 3.3.2 dev: true @@ -5228,14 +5232,14 @@ packages: resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} dev: true - /solid-js@1.8.17: - resolution: {integrity: sha512-E0FkUgv9sG/gEBWkHr/2XkBluHb1fkrHywUgA6o6XolPDCJ4g1HaLmQufcBBhiF36ee40q+HpG/vCZu7fLpI3Q==} + /solid-js@1.8.18: + resolution: {integrity: sha512-cpkxDPvO/AuKBugVv6xKFd1C9VC0XZMu4VtF56IlHoux8HgyW44uqNSWbozMnVcpIzHIhS3vVXPAVZYM26jpWw==} dependencies: csstype: 3.1.3 seroval: 1.0.7 seroval-plugins: 1.0.7(seroval@1.0.7) - /solid-refresh@0.6.3(solid-js@1.8.17): + /solid-refresh@0.6.3(solid-js@1.8.18): resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} peerDependencies: solid-js: ^1.3 @@ -5243,7 +5247,7 @@ packages: '@babel/generator': 7.24.7 '@babel/helper-module-imports': 7.24.7 '@babel/types': 7.24.7 - solid-js: 1.8.17 + solid-js: 1.8.18 transitivePeerDependencies: - supports-color dev: true @@ -5488,11 +5492,11 @@ packages: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.1 - postcss: 8.4.38 - postcss-import: 15.1.0(postcss@8.4.38) - postcss-js: 4.0.1(postcss@8.4.38) - postcss-load-config: 4.0.2(postcss@8.4.38) - postcss-nested: 6.0.1(postcss@8.4.38) + postcss: 8.4.39 + postcss-import: 15.1.0(postcss@8.4.39) + postcss-js: 4.0.1(postcss@8.4.39) + postcss-load-config: 4.0.2(postcss@8.4.39) + postcss-nested: 6.0.1(postcss@8.4.39) postcss-selector-parser: 6.1.0 resolve: 1.22.8 sucrase: 3.35.0 @@ -5753,7 +5757,7 @@ packages: pkg-types: 1.1.1 scule: 1.3.0 strip-literal: 2.1.0 - unplugin: 1.10.1 + unplugin: 1.10.2 transitivePeerDependencies: - rollup dev: true @@ -5782,14 +5786,14 @@ packages: '@vueuse/core': optional: true dependencies: - '@antfu/utils': 0.7.8 + '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.1.0(rollup@2.79.1) fast-glob: 3.3.2 local-pkg: 0.5.0 magic-string: 0.30.10 - minimatch: 9.0.4 + minimatch: 9.0.5 unimport: 3.7.2(rollup@2.79.1) - unplugin: 1.10.1 + unplugin: 1.10.2 transitivePeerDependencies: - rollup dev: true @@ -5815,18 +5819,18 @@ packages: optional: true dependencies: '@antfu/install-pkg': 0.3.3 - '@antfu/utils': 0.7.8 + '@antfu/utils': 0.7.10 '@iconify/utils': 2.1.25 debug: 4.3.5 kolorist: 1.8.0 local-pkg: 0.5.0 - unplugin: 1.10.1 + unplugin: 1.10.2 transitivePeerDependencies: - supports-color dev: true - /unplugin@1.10.1: - resolution: {integrity: sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==} + /unplugin@1.10.2: + resolution: {integrity: sha512-KuPqnjU4HBcrSwmQatfdc5hU4xzaQrhoKqCKylwmLnbBvqj5udXL8cHrkOuYDoI4ESCwJIiAIKMujroIUKLgow==} engines: {node: '>=14.0.0'} dependencies: acorn: 8.12.0 @@ -5872,7 +5876,7 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /vite-plugin-pwa@0.20.0(vite@5.3.1)(workbox-build@7.1.1)(workbox-window@7.1.0): + /vite-plugin-pwa@0.20.0(vite@5.3.2)(workbox-build@7.1.1)(workbox-window@7.1.0): resolution: {integrity: sha512-/kDZyqF8KqoXRpMUQtR5Atri/7BWayW8Gp7Kz/4bfstsV6zSFTxjREbXZYL7zSuRL40HGA+o2hvUAFRmC+bL7g==} engines: {node: '>=16.0.0'} peerDependencies: @@ -5887,14 +5891,14 @@ packages: debug: 4.3.5 fast-glob: 3.3.2 pretty-bytes: 6.1.1 - vite: 5.3.1 + vite: 5.3.2 workbox-build: 7.1.1 workbox-window: 7.1.0 transitivePeerDependencies: - supports-color dev: true - /vite-plugin-solid@2.10.2(solid-js@1.8.17)(vite@5.3.1): + /vite-plugin-solid@2.10.2(solid-js@1.8.18)(vite@5.3.2): resolution: {integrity: sha512-AOEtwMe2baBSXMXdo+BUwECC8IFHcKS6WQV/1NEd+Q7vHPap5fmIhLcAzr+DUJ04/KHx/1UBU0l1/GWP+rMAPQ==} peerDependencies: '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* @@ -5906,18 +5910,18 @@ packages: dependencies: '@babel/core': 7.24.7 '@types/babel__core': 7.20.5 - babel-preset-solid: 1.8.17(@babel/core@7.24.7) + babel-preset-solid: 1.8.18(@babel/core@7.24.7) merge-anything: 5.1.7 - solid-js: 1.8.17 - solid-refresh: 0.6.3(solid-js@1.8.17) - vite: 5.3.1 - vitefu: 0.2.5(vite@5.3.1) + solid-js: 1.8.18 + solid-refresh: 0.6.3(solid-js@1.8.18) + vite: 5.3.2 + vitefu: 0.2.5(vite@5.3.2) transitivePeerDependencies: - supports-color dev: true - /vite@5.3.1: - resolution: {integrity: sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==} + /vite@5.3.2: + resolution: {integrity: sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -5945,13 +5949,13 @@ packages: optional: true dependencies: esbuild: 0.21.5 - postcss: 8.4.38 + postcss: 8.4.39 rollup: 4.18.0 optionalDependencies: fsevents: 2.3.3 dev: true - /vitefu@0.2.5(vite@5.3.1): + /vitefu@0.2.5(vite@5.3.2): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -5959,7 +5963,7 @@ packages: vite: optional: true dependencies: - vite: 5.3.1 + vite: 5.3.2 dev: true /webidl-conversions@3.0.1: @@ -6169,36 +6173,37 @@ packages: workbox-core: 7.1.0 dev: true - /workerd@1.20240610.1: - resolution: {integrity: sha512-Rtut5GrsODQMh6YU43b9WZ980Wd05Ov1/ds88pT/SoetmXFBvkBzdRfiHiATv+azmGX8KveE0i/Eqzk/yI01ug==} + /workerd@1.20240620.1: + resolution: {integrity: sha512-Qoq+RrFNk4pvEO+kpJVn8uJ5TRE9YJx5jX5pC5LjdKlw1XeD8EdXt5k0TbByvWunZ4qgYIcF9lnVxhcDFo203g==} engines: {node: '>=16'} hasBin: true requiresBuild: true optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20240610.1 - '@cloudflare/workerd-darwin-arm64': 1.20240610.1 - '@cloudflare/workerd-linux-64': 1.20240610.1 - '@cloudflare/workerd-linux-arm64': 1.20240610.1 - '@cloudflare/workerd-windows-64': 1.20240610.1 + '@cloudflare/workerd-darwin-64': 1.20240620.1 + '@cloudflare/workerd-darwin-arm64': 1.20240620.1 + '@cloudflare/workerd-linux-64': 1.20240620.1 + '@cloudflare/workerd-linux-arm64': 1.20240620.1 + '@cloudflare/workerd-windows-64': 1.20240620.1 dev: true - /wrangler@3.61.0: - resolution: {integrity: sha512-feVAp0986x9xL3Dc1zin0ZVXKaqzp7eZur7iPLnpEwjG1Xy4dkVEZ5a1LET94Iyejt1P+EX5lgGcz63H7EfzUw==} + /wrangler@3.62.0: + resolution: {integrity: sha512-TM1Bd8+GzxFw/JzwsC3i/Oss4LTWvIEWXXo1vZhx+7PHcsxdbnQGBBwPurHNJDSu2Pw22+2pCZiUGKexmgJksw==} engines: {node: '>=16.17.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20240605.0 + '@cloudflare/workers-types': ^4.20240620.0 peerDependenciesMeta: '@cloudflare/workers-types': optional: true dependencies: - '@cloudflare/kv-asset-handler': 0.3.3 + '@cloudflare/kv-asset-handler': 0.3.4 '@esbuild-plugins/node-globals-polyfill': 0.2.3(esbuild@0.17.19) '@esbuild-plugins/node-modules-polyfill': 0.2.2(esbuild@0.17.19) blake3-wasm: 2.1.5 chokidar: 3.6.0 + date-fns: 3.6.0 esbuild: 0.17.19 - miniflare: 3.20240610.1 + miniflare: 3.20240620.0 nanoid: 3.3.7 path-to-regexp: 6.2.2 resolve: 1.22.8 diff --git a/app/src/app/components/background.tsx b/app/src/app/components/background.tsx index 44def2c50..1b7da9bd4 100644 --- a/app/src/app/components/background.tsx +++ b/app/src/app/components/background.tsx @@ -40,50 +40,47 @@ const texts = [ "absolute scarcity", ]; -export const LOCAL_STORAGE_MARQUEE_KEY = "bg-marquee"; - export function Background({ - marquee: on, + mode, + opacity, focused, }: { - marquee: Accessor; + mode: SL<"Scroll" | "Static">; + opacity: SL<{ text: string; value: number }>; focused: Accessor; }) { - createEffect(() => { - if (on()) { - localStorage.removeItem(LOCAL_STORAGE_MARQUEE_KEY); - } else { - localStorage.setItem(LOCAL_STORAGE_MARQUEE_KEY, "false"); - } - }); - return ( <> -
+
- - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + +
@@ -97,10 +94,10 @@ export function Background({ } function Line({ - on, + mode, focused, }: { - on: Accessor; + mode: SL<"Scroll" | "Static">; focused: Accessor; }) { const shuffled = shuffle([...texts]); @@ -109,17 +106,17 @@ function Line({ return (
- +
); } function TextWrapper({ joined, - on, + mode, focused, }: { - on: Accessor; + mode: SL<"Scroll" | "Static">; focused: Accessor; joined: string; }) { @@ -128,7 +125,7 @@ function TextWrapper({ const wasOnceOn = createRWS(false); createEffect(() => { - if (!wasOnceOn() && on()) { + if (!wasOnceOn() && mode.selected() === "Scroll") { wasOnceOn.set(true); } }); @@ -140,7 +137,10 @@ function TextWrapper({ ...(wasOnceOn() ? { animation: `marquee ${seconds}s linear infinite`, - "animation-play-state": focused() && on() ? "running" : "paused", + "animation-play-state": + focused() && mode.selected() === "Scroll" + ? "running" + : "paused", } : {}), }} diff --git a/app/src/app/components/frames/box.tsx b/app/src/app/components/frames/box.tsx index 14750ecb8..bd2617230 100644 --- a/app/src/app/components/frames/box.tsx +++ b/app/src/app/components/frames/box.tsx @@ -1,7 +1,4 @@ -import { createResizeObserver } from "@solid-primitives/resize-observer"; - import { classPropToString } from "/src/solid/classes"; -import { createRWS } from "/src/solid/rws"; export function Box({ flex = true, @@ -9,45 +6,14 @@ export function Box({ padded = true, children, dark, - overflowY, classes, }: { flex?: boolean; absolute?: "top" | "bottom"; padded?: boolean; dark?: boolean; - overflowY?: boolean; classes?: string; } & ParentProps) { - const maybeScrollable = createRWS(undefined); - const scrollable = createRWS(false); - const showLeftArrow = createRWS(false); - const showRightArrow = createRWS(false); - - onMount(() => { - createResizeObserver(maybeScrollable, (_, el) => { - if (el !== maybeScrollable()) { - return; - } - - scrollable.set(() => el.scrollWidth > el.clientWidth); - - checkArrows(); - }); - }); - - function checkArrows() { - const offset = 20; - - const target = maybeScrollable()!; - - const left = target.scrollLeft; - const right = target.scrollWidth - target.scrollLeft - target.clientWidth; - - showLeftArrow.set(() => left > offset); - showRightArrow.set(() => right > offset); - } - return (
- - {(obj) => ( - -
- -
-
- - )} - -
diff --git a/app/src/app/components/frames/chart/components/actions.tsx b/app/src/app/components/frames/chart/components/actions.tsx index 142e65ab6..0c028d001 100644 --- a/app/src/app/components/frames/chart/components/actions.tsx +++ b/app/src/app/components/frames/chart/components/actions.tsx @@ -17,7 +17,7 @@ export function Actions({ ); return ( -
+
{(fullscreen) => (
+ ); } diff --git a/app/src/app/components/frames/chart/components/timeScale.tsx b/app/src/app/components/frames/chart/components/timeScale.tsx index 9b28961c2..a6eaa61b3 100644 --- a/app/src/app/components/frames/chart/components/timeScale.tsx +++ b/app/src/app/components/frames/chart/components/timeScale.tsx @@ -1,63 +1,134 @@ -import { chartState } from "/src/scripts/lightweightCharts/chart/state"; import { GENESIS_DAY } from "/src/scripts/lightweightCharts/chart/whitespace"; import { ONE_DAY_IN_MS } from "/src/scripts/utils/time"; +import { classPropToString } from "/src/solid/classes"; import { Box } from "../../box"; +import { Scrollable } from "../../scrollable"; -export function TimeScale() { +export function TimeScale({ charts }: { charts: RWS }) { const today = new Date(); + const disabled = createMemo(() => charts().length === 0); + return ( - - - - - - - - - - - index + 2009) - .reverse()} - > - {(year) => ( - - )} - + + + + + + + + + + + + + index + 2009) + .reverse()} + > + {(year) => ( + + )} + + ); } -function Button(props: ParentProps & { onClick: VoidFunction }) { +function Button({ + onClick, + disabled, + children, +}: ParentProps & { onClick: VoidFunction; disabled: Accessor }) { return ( ); } -function setTimeScale({ days, year }: { days?: number; year?: number }) { +function setTimeScale({ + charts, + days, + year, +}: { + charts: RWS; + days?: number; + year?: number; +}) { let from = new Date(); let to = new Date(); @@ -70,12 +141,10 @@ function setTimeScale({ days, year }: { days?: number; year?: number }) { from = new Date(GENESIS_DAY); } - setRange({ - from: (from.getTime() / 1000) as Time, - to: (to.getTime() / 1000) as Time, - }); -} - -function setRange(range: TimeRange) { - chartState.chart?.timeScale().setVisibleRange(range); + charts()[0] + .timeScale() + .setVisibleRange({ + from: (from.getTime() / 1000) as Time, + to: (to.getTime() / 1000) as Time, + }); } diff --git a/app/src/app/components/frames/chart/components/title.tsx b/app/src/app/components/frames/chart/components/title.tsx index 864b60a68..5bf5374df 100644 --- a/app/src/app/components/frames/chart/components/title.tsx +++ b/app/src/app/components/frames/chart/components/title.tsx @@ -1,11 +1,9 @@ export function Title({ presets }: { presets: Presets }) { return ( -
+
-

{`/ ${[...presets.selected().path.map(({ name }) => name), presets.selected().name].join(" / ")}`}

-

- {presets.selected().title} -

+

{`/ ${[...presets.selected().path.map(({ name }) => name), presets.selected().name].join(" / ")}`}

+

{presets.selected().title}

); diff --git a/app/src/app/components/frames/chart/index.tsx b/app/src/app/components/frames/chart/index.tsx index b16d6ee4a..7781e0a8a 100644 --- a/app/src/app/components/frames/chart/index.tsx +++ b/app/src/app/components/frames/chart/index.tsx @@ -27,6 +27,10 @@ export function ChartFrame({ }) { const legend = createRWS([]); + const charts = createRWS([]); + + const div = createRWS(undefined); + const Chart = lazy(() => import("./components/chart").then((d) => ({ default: d.Chart })), ); @@ -35,29 +39,31 @@ export function ChartFrame({
- + - <div class="-mx-2 border-t border-orange-200/15" /> + <div class="border-lighter border-t" /> - <div class="flex pt-1.5"> + <div class="flex"> <Legend legend={legend} /> - <div class="-my-1.5 border-l border-orange-200/15 pr-1.5" /> + <div class="border-lighter border-l" /> <Actions presets={presets} qrcode={qrcode} fullscreen={fullscreen} /> </div> </Box> - <div class="-mt-2 min-h-0 flex-1"> + <div ref={div.set} class="-mr-2 -mt-2 flex min-h-0 flex-1 flex-col"> <Chart + parentDiv={div} + charts={charts} activeResources={activeResources} datasets={datasets} legendSetter={legend.set} @@ -65,7 +71,7 @@ export function ChartFrame({ /> </div> - <TimeScale /> + <TimeScale charts={charts} /> </div> ); } diff --git a/app/src/app/components/frames/favorites.tsx b/app/src/app/components/frames/favorites.tsx index 9f40b348b..df797a986 100644 --- a/app/src/app/components/frames/favorites.tsx +++ b/app/src/app/components/frames/favorites.tsx @@ -22,7 +22,7 @@ export function FavoritesFrame({ favorites. </Header> - <div class="-mx-4 border-t border-orange-200/10" /> + <div class="border-lighter -mx-4 border-t" /> <div class="space-y-0.5 py-1" diff --git a/app/src/app/components/frames/tree/components/file.tsx b/app/src/app/components/frames/folders/components/file.tsx similarity index 100% rename from app/src/app/components/frames/tree/components/file.tsx rename to app/src/app/components/frames/folders/components/file.tsx diff --git a/app/src/app/components/frames/tree/components/folder.tsx b/app/src/app/components/frames/folders/components/folder.tsx similarity index 78% rename from app/src/app/components/frames/tree/components/folder.tsx rename to app/src/app/components/frames/folders/components/folder.tsx index 93f8dd435..94fd66684 100644 --- a/app/src/app/components/frames/tree/components/folder.tsx +++ b/app/src/app/components/frames/folders/components/folder.tsx @@ -26,10 +26,10 @@ export function Folder({ name={name} icon={icon} onClick={onClick} - classes={() => (open() ? "text-orange-100/75" : "")} + classes={() => (open() ? "opacity-60" : "")} tail={() => ( <Show when={!open()}> - <span class="rounded-full bg-white bg-opacity-[0.075] px-2 py-0.5 text-xs text-neutral-400"> + <span class="rounded-full bg-orange-50/10 px-2 py-0.5 text-xs text-neutral-400"> {children} </span> </Show> diff --git a/app/src/app/components/frames/tree/components/tree.tsx b/app/src/app/components/frames/folders/components/tree.tsx similarity index 100% rename from app/src/app/components/frames/tree/components/tree.tsx rename to app/src/app/components/frames/folders/components/tree.tsx diff --git a/app/src/app/components/frames/tree/index.tsx b/app/src/app/components/frames/folders/index.tsx similarity index 92% rename from app/src/app/components/frames/tree/index.tsx rename to app/src/app/components/frames/folders/index.tsx index 77cf5b4fb..cb2da1373 100644 --- a/app/src/app/components/frames/tree/index.tsx +++ b/app/src/app/components/frames/folders/index.tsx @@ -8,7 +8,7 @@ import { Header } from "../header"; import { Number } from "../number"; import { Tree } from "./components/tree"; -export function TreeFrame({ +export function FoldersFrame({ presets, selectedFrame, }: { @@ -25,7 +25,7 @@ export function TreeFrame({ <div class="relative flex size-full flex-1 flex-col" style={{ - display: selectedFrame() !== "Tree" ? "none" : undefined, + display: selectedFrame() !== "Folders" ? "none" : undefined, }} > <div class="flex-1 overflow-y-auto"> @@ -35,7 +35,7 @@ export function TreeFrame({ tree like structure. </Header> - <div class="-mx-4 border-t border-orange-200/10" /> + <div class="border-lighter -mx-4 border-t" /> <Tree tree={presets.tree} diff --git a/app/src/app/components/frames/header.tsx b/app/src/app/components/frames/header.tsx index f77894ccf..1e0e4e910 100644 --- a/app/src/app/components/frames/header.tsx +++ b/app/src/app/components/frames/header.tsx @@ -2,7 +2,7 @@ export function Header({ title, children }: { title: string } & ParentProps) { return ( <div> <h3 class="text-lg font-bold md:text-xl">{title}</h3> - <p class="text-orange-100/75">{children}</p> + <p class="text-orange-950/60 dark:text-orange-100/75">{children}</p> </div> ); } diff --git a/app/src/app/components/frames/history.tsx b/app/src/app/components/frames/history.tsx index 453469528..0ac785e47 100644 --- a/app/src/app/components/frames/history.tsx +++ b/app/src/app/components/frames/history.tsx @@ -36,8 +36,8 @@ export function HistoryFrame({ presets.history()[index() - 1].date.toJSON().split("T")[0] } > - <div class="sticky top-[-0.5rem] z-10 -mx-4 py-2"> - <div class="border-y border-orange-200/10 bg-[rgb(25,15,15)] p-2"> + <div class="sticky top-[calc(-0.5rem-1px)] z-10 -mx-4 py-2"> + <div class="border-lighter border-y bg-[#F4EAE3] p-2 dark:bg-[rgb(25,15,15)]"> <p class="ml-2"> <Switch fallback={date.toLocaleDateString()}> <Match diff --git a/app/src/app/components/frames/line.tsx b/app/src/app/components/frames/line.tsx index df1064098..dafa9ea1d 100644 --- a/app/src/app/components/frames/line.tsx +++ b/app/src/app/components/frames/line.tsx @@ -45,9 +45,7 @@ export function Line({ title={name} > <For each={new Array(depth)}> - {() => ( - <span class="ml-1 h-8 w-3 flex-none border-l border-orange-200/10" /> - )} + {() => <span class="border-lighter ml-1 h-8 w-3 flex-none border-l" />} </For> <Show when={icon}> {(icon) => ( @@ -68,10 +66,7 @@ export function Line({ ])} > <Show when={header}> - <span - class="truncate text-xs text-white text-opacity-50" - innerHTML={header} - /> + <span class="truncate text-xs opacity-50" innerHTML={header} /> </Show> <span class="space-x-1 truncate"> <span innerHTML={name} /> diff --git a/app/src/app/components/frames/scrollable.tsx b/app/src/app/components/frames/scrollable.tsx new file mode 100644 index 000000000..1cf381610 --- /dev/null +++ b/app/src/app/components/frames/scrollable.tsx @@ -0,0 +1,126 @@ +import { createResizeObserver } from "@solid-primitives/resize-observer"; + +import { touchScreen } from "/src/env"; +import { classPropToString } from "/src/solid/classes"; +import { createRWS } from "/src/solid/rws"; + +export function Scrollable({ + children, + classes, +}: { + classes?: string; +} & ParentProps) { + const maybeScrollable = createRWS<HTMLDivElement | undefined>(undefined); + const scrollable = createRWS(false); + const showLeftArrow = createRWS(false); + const showRightArrow = createRWS(false); + + onMount(() => { + createResizeObserver(maybeScrollable, (_, el) => { + if (el !== maybeScrollable()) { + return; + } + + scrollable.set(() => el.scrollWidth > el.clientWidth); + + checkArrows(); + }); + }); + + function checkArrows() { + const target = maybeScrollable()!; + + const left = target.scrollLeft; + const right = + target.scrollWidth - Math.ceil(target.scrollLeft + target.clientWidth); + + showLeftArrow.set(() => left > 0); + showRightArrow.set(() => right > 0); + } + + return ( + <div class="relative min-w-0 flex-1"> + <For + each={[ + { + showArrow: showLeftArrow, + side: "left-0", + order: "", + buttonPadding: "pl-2", + iconPadding: "pr-0.5", + scrollMultiplier: -1, + chevronIcon: IconTablerChevronLeft, + gradientDirection: "bg-gradient-to-r", + }, + { + showArrow: showRightArrow, + side: "right-0", + order: "order-2", + buttonPadding: "pr-2", + iconPadding: "pl-0.5", + scrollMultiplier: 1, + chevronIcon: IconTablerChevronRight, + gradientDirection: "bg-gradient-to-l", + }, + ]} + > + {(obj) => ( + <Show when={scrollable() && obj.showArrow()}> + <div + class={[ + obj.side, + "pointer-events-none absolute bottom-0 top-0 flex transition-opacity duration-200 ease-in-out", + ].join(" ")} + > + <Show when={!touchScreen}> + <div + class={[ + obj.order, + obj.buttonPadding, + "pointer-events-auto flex h-full items-center bg-stone-100/75 dark:bg-stone-900/75", + ].join(" ")} + > + <button + onClick={() => { + maybeScrollable()?.scrollBy({ + left: Math.floor( + maybeScrollable()!.clientWidth * + obj.scrollMultiplier * + 0.75, + ), + behavior: "smooth", + }); + }} + class="border-light rounded-full border bg-stone-100 p-0.5 shadow transition hover:scale-110 active:scale-100 dark:bg-stone-900" + > + <Dynamic + component={obj.chevronIcon} + class={[`size-5 ${obj.iconPadding}`]} + /> + </button> + </div> + </Show> + <div + class={[ + obj.gradientDirection, + "h-full w-8 from-stone-100/75 to-transparent dark:from-stone-900/75", + ].join(" ")} + /> + </div> + </Show> + )} + </For> + + <div + ref={maybeScrollable.set} + onScroll={checkArrows} + class={classPropToString([ + "no-scrollbar flex w-full overflow-x-auto", + classes, + ])} + > + {children} + </div> + </div> + ); +} diff --git a/app/src/app/components/frames/search.tsx b/app/src/app/components/frames/search.tsx index d26092e73..04d9b4bc3 100644 --- a/app/src/app/components/frames/search.tsx +++ b/app/src/app/components/frames/search.tsx @@ -135,7 +135,7 @@ export function SearchFrame({ </p> <Show when={search()}> - <div class="-mx-4 border-t border-orange-200/10" /> + <div class="border-lighter -mx-4 border-t" /> <div class="py-1" @@ -176,7 +176,7 @@ export function SearchFrame({ value={search()} onInput={(event) => search.set(event.target.value)} /> - <span class="-mx-1 flex size-5 flex-none items-center justify-center rounded-md border border-white text-xs font-bold"> + <span class="-mx-1 flex size-5 flex-none items-center justify-center rounded-md border border-current text-xs font-bold"> <IconTablerSlash /> </span> </div> diff --git a/app/src/app/components/frames/settings.tsx b/app/src/app/components/frames/settings.tsx index 77039193d..5603622e0 100644 --- a/app/src/app/components/frames/settings.tsx +++ b/app/src/app/components/frames/settings.tsx @@ -1,15 +1,30 @@ import { version } from "/src/../package.json"; +import { classPropToString } from "/src/solid/classes"; import { Header } from "./header"; export function SettingsFrame({ - marquee, selectedFrame, + appTheme, + backgroundMode, + backgroundOpacity, }: { - marquee: RWS<boolean>; selectedFrame: Accessor<FrameName>; + appTheme: SL<"System" | "Dark" | "Light">; + backgroundMode: SL<"Scroll" | "Static">; + backgroundOpacity: SL<{ text: string; value: number }>; }) { - const value = marquee(); + createEffect(() => { + if ( + appTheme.selected() === "Dark" || + (appTheme.selected() === "System" && + window.matchMedia("(prefers-color-scheme: dark)").matches) + ) { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + }); return ( <div @@ -19,28 +34,102 @@ export function SettingsFrame({ }} > <div class="space-y-4 p-4"> - <Header title="Settings">And other stuff.</Header> + <Header title="Settings"> + And other stuff <strong class="italic">NOT</strong> transmitted by + relays. + </Header> - <div class="-mx-4 border-t border-orange-200/10" /> + <div class="border-lighter -mx-4 border-t" /> - <div class="space-y-2"> - <p>Background</p> - <div>Opacity</div> - <div> - <label class="switch"> - Scroll - <input - type="checkbox" - checked={value} - onChange={(event) => marquee.set(event.target.checked || false)} - /> - <span class="slider"></span> - </label> - </div> - <hr class="border-t border-orange-200/20" /> - <p>Version: {version}</p> + <div class="space-y-4"> + <p class="text-base font-medium">General</p> + + <RadioGroup + title="Theme" + ariaTitle="App's theme" + description="Options for the app's theme" + sl={appTheme} + /> </div> + + <div class="border-lighter -mx-4 border-t" /> + + <div class="space-y-4"> + <p class="text-base font-medium">Background</p> + + <RadioGroup + title="Mode" + ariaTitle="Background mode" + description="Options for how the background in displayed" + sl={backgroundMode} + /> + + <RadioGroup + title="Opacity" + ariaTitle="Background mode" + description="Options for the opacity of the text in the background" + sl={backgroundOpacity} + /> + </div> + + <hr class="border-lighter -mx-4 border-t" /> + <p class="text-center"> + <span class="opacity-50">Version:</span> {version} + </p> </div> </div> ); } + +function RadioGroup< + T extends + | string + | { + text: string; + value: number; + }, +>({ + title, + sl, + ariaTitle, + description, +}: { + title: string; + ariaTitle: string; + description: string; + sl: SL<T>; +}) { + return ( + <fieldset aria-label={`Choose an option for: ${ariaTitle}`}> + <p class="pb-0.5">{title}</p> + + <p class="pb-1 text-sm opacity-50">{description}</p> + + <div class="border-superlight -mx-2 mt-2 flex gap-1.5 rounded-lg border bg-stone-400/30 p-1.5 backdrop-blur-[2px] dark:bg-stone-950/75"> + <For each={sl.list()}> + {(value) => ( + <label + class={classPropToString([ + value === sl.selected() + ? "border-lighter bg-orange-50/75 shadow dark:bg-orange-200/10" + : "border-transparent", + "flex cursor-pointer select-none items-center justify-center rounded-md border px-3 py-1.5 font-medium hover:bg-orange-50 focus:outline-none active:scale-95 active:bg-orange-50 dark:hover:bg-orange-200/20 dark:active:bg-orange-200/10 sm:flex-1", + ])} + > + <input + type="radio" + name={`${title}-option`} + value={typeof value === "object" ? value.value : value} + class="sr-only" + onClick={() => { + sl.select(value); + }} + /> + <span>{typeof value === "object" ? value.text : value}</span> + </label> + )} + </For> + </div> + </fieldset> + ); +} diff --git a/app/src/app/components/qrcode.tsx b/app/src/app/components/qrcode.tsx index 075105380..ffac43c4d 100644 --- a/app/src/app/components/qrcode.tsx +++ b/app/src/app/components/qrcode.tsx @@ -1,25 +1,34 @@ +import { touchScreen } from "/src/env"; + export function Qrcode({ qrcode }: { qrcode: RWS<string> }) { return ( <Show when={qrcode()}> <div - class="absolute inset-0 z-50 flex size-full justify-center bg-black" + class="absolute inset-0 z-50 flex size-full items-center justify-center bg-black/50 backdrop-blur-md" onClick={() => { qrcode.set(""); }} > - <div class="flex size-full max-w-md flex-col items-center justify-center bg-black px-8 py-16 text-lg"> - <p class="pb-16 text-2xl font-bold">Share</p> + <div class="flex size-full max-h-[80dvh] max-w-md flex-col justify-center space-y-8 px-8 pb-8 text-base"> + <p class="pb-4 text-center text-3xl font-bold">Share</p> - <div class="flex min-h-0 w-full flex-1 flex-col"> - <p>You can scan the following QR Code with a phone:</p> + <p> + To share this page, you can either send the following QR Code with a + phone: + </p> + <div class="flex min-h-0 w-full flex-1 flex-col items-center justify-center"> <img class="aspect-square min-h-0 flex-1 grow object-contain" + onClick={(event) => { + event?.stopPropagation(); + }} src={qrcode()} style={{ "image-rendering": "pixelated" }} /> </div> + <div> - <p>Or if you prefer you can send this link instead:</p> + <p>Or if you prefer you can share this link instead:</p> <a onClick={(event) => { event.stopPropagation(); @@ -29,6 +38,11 @@ export function Qrcode({ qrcode }: { qrcode: RWS<string> }) { {location.href} </a> </div> + + <p> + {touchScreen ? "Touch" : "Click"} anywhere but on the QR Code to + exit. + </p> </div> </div> </Show> diff --git a/app/src/app/components/strip/components/anchorLogo.tsx b/app/src/app/components/strip/components/anchorLogo.tsx index 303adbdf6..027a47f2f 100644 --- a/app/src/app/components/strip/components/anchorLogo.tsx +++ b/app/src/app/components/strip/components/anchorLogo.tsx @@ -1,7 +1,7 @@ export function AnchorLogo() { return ( <a - class="inline-flex justify-center rounded-lg bg-gradient-to-br from-orange-500 to-orange-800 p-4 text-white" + class="inline-flex justify-center rounded-lg bg-gradient-to-br from-orange-300 to-orange-600 p-4 text-orange-50 shadow dark:from-orange-500 dark:to-orange-800 dark:text-orange-50" href="https://app.satonomics.xyz" title="Reload" > diff --git a/app/src/app/components/strip/components/buttonTree.tsx b/app/src/app/components/strip/components/buttonFolders.tsx similarity index 98% rename from app/src/app/components/strip/components/buttonTree.tsx rename to app/src/app/components/strip/components/buttonFolders.tsx index 15826b582..3d842589a 100644 --- a/app/src/app/components/strip/components/buttonTree.tsx +++ b/app/src/app/components/strip/components/buttonFolders.tsx @@ -1,13 +1,13 @@ import { Button } from "./button"; -export function ButtonTree({ +export function ButtonFolders({ selected, setSelected, }: { selected: Accessor<FrameName>; setSelected: Setter<FrameName>; }) { - const frameName: FrameName = "Tree"; + const frameName: FrameName = "Folders"; return ( <Button diff --git a/app/src/app/components/strip/components/clickable.tsx b/app/src/app/components/strip/components/clickable.tsx index 113fe458d..982172229 100644 --- a/app/src/app/components/strip/components/clickable.tsx +++ b/app/src/app/components/strip/components/clickable.tsx @@ -20,10 +20,10 @@ export function Clickable({ class={classPropToString([ !href ? selected?.() - ? "bg-orange-200/10" - : "text-orange-100/50" - : "text-orange-300/70", - "select-none rounded-lg p-3.5 hover:bg-orange-200/10 hover:text-orange-400 hover:opacity-100 active:scale-90", + ? "bg-orange-800/10 dark:bg-orange-200/10" + : "text-orange-900/50 dark:text-orange-100/50" + : "text-opacity-70 dark:text-opacity-70", + "select-none rounded-lg p-3.5 hover:bg-orange-800/10 hover:text-orange-600 hover:opacity-100 active:scale-90 dark:hover:bg-orange-200/10 dark:hover:text-orange-400", ])} title={title} onClick={onClick} diff --git a/app/src/app/components/strip/index.tsx b/app/src/app/components/strip/index.tsx index 334e889de..0d3d675d3 100644 --- a/app/src/app/components/strip/index.tsx +++ b/app/src/app/components/strip/index.tsx @@ -5,10 +5,10 @@ import { AnchorLogo } from "./components/anchorLogo"; import { AnchorNostr } from "./components/anchorNostr"; import { ButtonChart } from "./components/buttonChart"; import { ButtonFavorites } from "./components/buttonFavorites"; +import { ButtonFolders } from "./components/buttonFolders"; import { ButtonHistory } from "./components/buttonHistory"; import { ButtonSearch } from "./components/buttonSearch"; import { ButtonSettings } from "./components/buttonSettings"; -import { ButtonTree } from "./components/buttonTree"; export function StripDesktop({ selected, @@ -21,7 +21,7 @@ export function StripDesktop({ <> <AnchorLogo /> - <ButtonTree selected={selected} setSelected={setSelected} /> + <ButtonFolders selected={selected} setSelected={setSelected} /> <ButtonFavorites selected={selected} setSelected={setSelected} /> <ButtonSearch selected={selected} setSelected={setSelected} /> <ButtonHistory selected={selected} setSelected={setSelected} /> @@ -49,7 +49,7 @@ export function StripMobile({ return ( <> <ButtonChart selected={selected} setSelected={setSelected} /> - <ButtonTree selected={selected} setSelected={setSelected} /> + <ButtonFolders selected={selected} setSelected={setSelected} /> <ButtonFavorites selected={selected} setSelected={setSelected} /> <ButtonSearch selected={selected} setSelected={setSelected} /> <ButtonHistory selected={selected} setSelected={setSelected} /> diff --git a/app/src/app/index.tsx b/app/src/app/index.tsx index fd2734fa7..b899189f4 100644 --- a/app/src/app/index.tsx +++ b/app/src/app/index.tsx @@ -5,7 +5,7 @@ import { createDatasets } from "../scripts/datasets"; import { chartState } from "../scripts/lightweightCharts/chart/state"; import { setTimeScale } from "../scripts/lightweightCharts/chart/time"; import { createPresets } from "../scripts/presets"; -import { priceToUSLocale } from "../scripts/utils/locale"; +import { createSL } from "../scripts/utils/selectableList/static"; import { sleep } from "../scripts/utils/sleep"; import { readBooleanFromStorage, @@ -14,12 +14,12 @@ import { import { readBooleanURLParam, writeURLParam } from "../scripts/utils/urlParams"; import { webSockets } from "../scripts/ws"; import { classPropToString } from "../solid/classes"; -import { Background, LOCAL_STORAGE_MARQUEE_KEY } from "./components/background"; +import { Background } from "./components/background"; import { ChartFrame } from "./components/frames/chart"; import { FavoritesFrame } from "./components/frames/favorites"; +import { FoldersFrame } from "./components/frames/folders"; import { HistoryFrame } from "./components/frames/history"; import { SettingsFrame } from "./components/frames/settings"; -import { TreeFrame } from "./components/frames/tree"; import { Qrcode } from "./components/qrcode"; import { StripDesktop, StripMobile } from "./components/strip"; import { Update } from "./components/update"; @@ -31,9 +31,52 @@ export const INPUT_PRESET_SEARCH_ID = "input-search-preset"; export function App() { const tabFocused = createRWS(true); - const qrcode = createRWS(""); + const appTheme = createSL(["System", "Dark", "Light"] as const, { + saveable: { + key: "app-theme", + mode: "localStorage", + }, + defaultIndex: 0, + }); + + const backgroundMode = createSL(["Scroll", "Static"] as const, { + saveable: { + key: "bg-mode", + mode: "localStorage", + }, + defaultIndex: 0, + }); + + const backgroundOpacity = createSL( + [ + { + text: "Strong", + value: 0.0444, + }, + { + text: "Normal", + value: 0.0333, + }, + { + text: "Light", + value: 0.0222, + }, + { + text: "Subtle", + value: 0.0111, + }, + ] as const, + { + saveable: { + key: "bg-text-opacity", + mode: "localStorage", + }, + defaultIndex: 2, + }, + ); + const fullscreen = createRWS( readBooleanURLParam(LOCAL_STORAGE_FULLSCREEN) || readBooleanFromStorage(LOCAL_STORAGE_FULLSCREEN) || @@ -48,7 +91,7 @@ export function App() { window.addEventListener("resize", windowResizeCallback); onCleanup(() => window.removeEventListener("resize", windowResizeCallback)); - const windowSizeIsAtLeastMedium = createMemo(() => windowWidth() >= 720); + const windowSizeIsAtLeastMedium = createMemo(() => windowWidth() >= 768); const minBarWidth = 384; const barWidth = createRWS( @@ -73,14 +116,12 @@ export function App() { const selectedFrame = createMemo(() => windowSizeIsAtLeastMedium() && _selectedFrame() === "Chart" - ? "Tree" + ? "Folders" : _selectedFrame(), ); const presets = createPresets(); - const marquee = createRWS(!localStorage.getItem(LOCAL_STORAGE_MARQUEE_KEY)); - const resizingBarStart = createRWS<number | undefined>(undefined); const resizingBarWidth = createRWS<number>(0); @@ -114,7 +155,7 @@ export function App() { console.log("close:", close); - document.title = `${priceToUSLocale(latest.close, false)} | Satonomics`; + document.title = `${latest.close.toLocaleString("en-us")} | Satonomics`; } }); }); @@ -163,7 +204,11 @@ export function App() { return ( <> - <Background marquee={marquee} focused={tabFocused} /> + <Background + focused={tabFocused} + mode={backgroundMode} + opacity={backgroundOpacity} + /> <div class="relative h-dvh selection:bg-orange-800" @@ -195,15 +240,15 @@ export function App() { <Qrcode qrcode={qrcode} /> <Update /> - <div class="md:short:p-0 flex size-full flex-col md:flex-row md:p-3"> + <div class="flex size-full flex-col md:flex-row md:p-3 md:short:p-0"> <Show when={!windowSizeIsAtLeastMedium() || !fullscreen()}> <div class={classPropToString([ - standalone && "border-t", - "md:short:hidden flex h-full flex-col overflow-hidden border-white/10 bg-gradient-to-b from-orange-500/10 to-orange-950/10 md:flex-row md:rounded-2xl md:border", + standalone && "border-t md:border-t-0", + "border-lighter flex h-full flex-col overflow-hidden bg-gradient-to-b from-orange-300/15 to-orange-400/15 dark:from-orange-500/10 dark:to-orange-950/10 md:flex-row md:rounded-2xl md:border md:shadow-md md:short:hidden", ])} > - <div class="hidden flex-col gap-2 border-r border-white/10 bg-black/30 p-3 backdrop-blur-sm md:flex"> + <div class="border-lighter hidden flex-col gap-2 border-r bg-orange-300/30 p-3 backdrop-blur-sm dark:bg-black/30 md:flex"> <StripDesktop selected={selectedFrame} setSelected={_selectedFrame.set} @@ -232,7 +277,7 @@ export function App() { /> </Show> - <TreeFrame presets={presets} selectedFrame={selectedFrame} /> + <FoldersFrame presets={presets} selectedFrame={selectedFrame} /> <FavoritesFrame presets={presets} selectedFrame={selectedFrame} @@ -240,15 +285,17 @@ export function App() { <SearchFrame presets={presets} selectedFrame={selectedFrame} /> <HistoryFrame presets={presets} selectedFrame={selectedFrame} /> <SettingsFrame - marquee={marquee} selectedFrame={selectedFrame} + appTheme={appTheme} + backgroundMode={backgroundMode} + backgroundOpacity={backgroundOpacity} /> </div> <div class={classPropToString([ standalone && "pb-6", - "short:hidden flex justify-between gap-3 border-t border-white/10 bg-black/30 p-2 backdrop-blur-sm md:hidden", + "border-lighter flex justify-between gap-3 border-t bg-black/30 p-2 backdrop-blur-sm sm:justify-around md:hidden short:hidden", ])} > <StripMobile @@ -261,7 +308,7 @@ export function App() { <Show when={!fullscreen()}> <div - class="short:hidden mx-[3px] my-8 hidden w-[6px] cursor-col-resize items-center justify-center rounded-full bg-orange-100 opacity-0 hover:opacity-50 md:block" + class="mx-[3px] my-8 hidden w-[6px] cursor-col-resize items-center justify-center rounded-full bg-orange-900 opacity-0 hover:opacity-50 dark:bg-orange-100 md:block short:hidden" onMouseDown={(event) => { resizeInitialRange.set(chartState.range); @@ -279,8 +326,8 @@ export function App() { } }} onDblClick={() => { - resizeInitialRange.set(chartState.range); barWidth.set(0); + setTimeScale(resizeInitialRange()); }} /> diff --git a/app/src/app/types.d.ts b/app/src/app/types.d.ts index 9b3b764d8..9d3740d06 100644 --- a/app/src/app/types.d.ts +++ b/app/src/app/types.d.ts @@ -1,6 +1,6 @@ type FrameName = | "Chart" - | "Tree" + | "Folders" | "Favorites" | "Search" | "History" diff --git a/app/src/scripts/datasets/types.d.ts b/app/src/scripts/datasets/types.d.ts index 1b682a999..7ca681ed9 100644 --- a/app/src/scripts/datasets/types.d.ts +++ b/app/src/scripts/datasets/types.d.ts @@ -34,6 +34,9 @@ interface ResourceDataset< drop: VoidFunction; } +type AnyDataset<Scale extends ResourceScale> = Dataset<Scale> & + Partial<ResourceDataset<Scale>>; + interface FetchedResult< Scale extends ResourceScale, Type extends number | OHLC, diff --git a/app/src/scripts/lightweightCharts/chart/clean.ts b/app/src/scripts/lightweightCharts/chart/clean.ts deleted file mode 100644 index 2e3a5d246..000000000 --- a/app/src/scripts/lightweightCharts/chart/clean.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { chartState } from "./state"; - -export function cleanChart() { - console.log("chart: clean"); - - try { - chartState.chart?.remove(); - } catch {} - - chartState.chart = null; -} diff --git a/app/src/scripts/lightweightCharts/chart/create.ts b/app/src/scripts/lightweightCharts/chart/create.ts index cd60dd7ac..7c943c2b6 100644 --- a/app/src/scripts/lightweightCharts/chart/create.ts +++ b/app/src/scripts/lightweightCharts/chart/create.ts @@ -5,14 +5,14 @@ import { } from "lightweight-charts"; import { colors } from "../../utils/colors"; -import { priceToUSLocale } from "../../utils/locale"; -import { cleanChart } from "./clean"; +import { valueToString } from "../../utils/locale"; import { HorzScaleBehaviorHeight } from "./horzScaleBehavior"; -import { chartState } from "./state"; - -export function createChart(scale: ResourceScale) { - cleanChart(); +export function createChart( + scale: ResourceScale, + element: HTMLElement, + priceScaleOptions?: DeepPartialPriceScaleOptions, +) { console.log(`chart: create (scale: ${scale})`); const { white } = colors; @@ -29,19 +29,19 @@ export function createChart(scale: ResourceScale) { vertLines: { visible: false }, horzLines: { visible: false }, }, - leftPriceScale: { - // borderColor: white, - }, rightPriceScale: { - // borderColor: white, + borderColor: "#332F24", }, timeScale: { + borderColor: "#332F24", minBarSpacing: scale === "date" ? 0.05 : 0.005, shiftVisibleRangeOnNewBar: false, allowShiftVisibleRangeOnWhitespaceReplacement: false, }, handleScale: { - axisDoubleClickReset: false, + axisDoubleClickReset: { + time: false, + }, }, crosshair: { mode: CrosshairMode.Normal, @@ -55,17 +55,31 @@ export function createChart(scale: ResourceScale) { }, }, localization: { - priceFormatter: priceToUSLocale, + priceFormatter: valueToString, locale: "en-us", }, }; + let chart: IChartApi; + if (scale === "date") { - chartState.chart = createClassicChart("chart", options); + chart = createClassicChart(element, options); } else { const horzScaleBehavior = new HorzScaleBehaviorHeight(); // @ts-ignore - chartState.chart = createCustomChart("chart", horzScaleBehavior, options); + chart = createCustomChart(element, horzScaleBehavior, options); } + + chart.priceScale("right").applyOptions({ + ...priceScaleOptions, + scaleMargins: { + top: 0.05, + bottom: 0.05, + ...priceScaleOptions?.scaleMargins, + }, + minimumWidth: 78, + }); + + return chart; } diff --git a/app/src/scripts/lightweightCharts/chart/markers.ts b/app/src/scripts/lightweightCharts/chart/markers.ts index 923a5b642..6c1d5500d 100644 --- a/app/src/scripts/lightweightCharts/chart/markers.ts +++ b/app/src/scripts/lightweightCharts/chart/markers.ts @@ -1,5 +1,4 @@ import { colors } from "/src/scripts/utils/colors"; -import { priceToUSLocale } from "/src/scripts/utils/locale"; import { ONE_DAY_IN_MS } from "/src/scripts/utils/time"; import { chartState } from "./state"; @@ -101,12 +100,11 @@ export const setMinMaxMarkers = ({ candle && markers.push({ ...markerOptions, - // date: candle.date, number: candle.number, time: candle.time, color: lowerOpacity ? colors.darkWhite : colors.white, size: 0, - text: priceToUSLocale(value), + text: value.toLocaleString("en-us"), }) ); }, diff --git a/app/src/scripts/lightweightCharts/chart/price.ts b/app/src/scripts/lightweightCharts/chart/price.ts index ae10c2e1e..57b1f4a41 100644 --- a/app/src/scripts/lightweightCharts/chart/price.ts +++ b/app/src/scripts/lightweightCharts/chart/price.ts @@ -1,14 +1,8 @@ -import { createRWS } from "/src/solid/rws"; - import { colors } from "../../utils/colors"; -import { getNumberOfDaysBetweenTwoDates } from "../../utils/date"; -import { debounce } from "../../utils/debounce"; import { webSockets } from "../../ws"; import { createCandlesticksSeries } from "../series/creators/candlesticks"; import { createSeriesLegend } from "../series/creators/legend"; import { createLineSeries } from "../series/creators/line"; -import { setMinMaxMarkers } from "./markers"; -import { chartState } from "./state"; import { initTimeScale } from "./time"; export const PRICE_SCALE_MOMENTUM_ID = "momentum"; @@ -18,76 +12,32 @@ export const applyPriceSeries = < T extends SingleValueData, >({ chart, - datasets, preset, - dataset: _dataset, + dataset, + seriesType, + valuesSkipped, options, activeResources, }: { chart: IChartApi; - datasets: Datasets; preset: Preset; + valuesSkipped: Accessor<number>; + seriesType: Accessor<"Candlestick" | "Line">; activeResources: Accessor<Set<ResourceDataset<any, any>>>; - dataset?: Dataset<Scale, T>; + dataset: Dataset<Scale, T>; options?: PriceSeriesOptions; }) => { const id = options?.id || "price"; const title = options?.title || "Price"; - const dataset = _dataset || datasets[preset.scale].price; - const url = "url" in dataset ? (dataset as any).url : undefined; - const priceScaleOptions: DeepPartial<PriceScaleOptions> = { - ...(options?.halved - ? { - scaleMargins: { - top: 0.05, - bottom: 0.55, - }, - } - : {}), - ...(options?.id || options?.title - ? {} - : { - mode: 1, - }), + const priceScaleOptions: DeepPartialPriceScaleOptions = { + mode: 1, ...options?.priceScaleOptions, }; - const seriesType = createRWS( - checkIfUpClose(chart, chartState.range) || "Candlestick", - ); - - const debouncedCallback = debounce((range: TimeRange | null) => { - try { - seriesType.set((previous) => checkIfUpClose(chart, range) || previous); - } catch {} - }, 50); - - chart?.timeScale().subscribeVisibleTimeRangeChange(debouncedCallback); - - onCleanup( - () => - chart === chartState.chart && - chartState.chart - ?.timeScale() - .unsubscribeVisibleTimeRangeChange(debouncedCallback), - ); - - const lowerOpacity = options?.lowerOpacity || options?.halved || false; - - if (options?.halved) { - options.seriesOptions = { - ...options.seriesOptions, - priceScaleId: "left", - }; - } - - const [ohlcSeries, ohlcColors] = createCandlesticksSeries(chart, { - ...options, - lowerOpacity, - }); + const [ohlcSeries, ohlcColors] = createCandlesticksSeries(chart, options); const ohlcLegend = createSeriesLegend({ id, @@ -103,7 +53,7 @@ export const applyPriceSeries = < // --- - const lineColor = lowerOpacity ? colors.darkWhite : colors.white; + const lineColor = colors.white; const lineSeries = createLineSeries(chart, { color: lineColor, @@ -139,11 +89,10 @@ export const applyPriceSeries = < createEffect(() => { const values = dataset.values(); + // const values = computeDrawnSeriesValues(dataset.values(), valuesSkipped()); - if (values) { - lineSeries.setData(values); - ohlcSeries.setData(values); - } + lineSeries.setData(values); + ohlcSeries.setData(values); }); createEffect(() => { @@ -159,20 +108,3 @@ export const applyPriceSeries = < return { ohlcLegend, lineLegend }; }; - -function checkIfUpClose(chart: IChartApi, range?: TimeRange | null) { - if (!range) return undefined; - - const from = new Date(range.from); - const to = new Date(range.to); - - const width = chart.timeScale().width(); - - const difference = getNumberOfDaysBetweenTwoDates(from, to); - - return width / difference >= 2.05 - ? "Candlestick" - : width / difference <= 1.95 - ? "Line" - : undefined; -} diff --git a/app/src/scripts/lightweightCharts/chart/render.ts b/app/src/scripts/lightweightCharts/chart/render.ts deleted file mode 100644 index d4b36c83e..000000000 --- a/app/src/scripts/lightweightCharts/chart/render.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { createChart } from "./create"; -import { chartState } from "./state"; -import { setWhitespace } from "./whitespace"; - -export function renderChart({ - datasets, - legendSetter, - preset, - activeResources, -}: { - datasets: Datasets; - legendSetter: Setter<PresetLegend>; - preset: Preset; - activeResources: Accessor<Set<ResourceDataset<any, any>>>; -}) { - const scale = preset.scale; - - createChart(scale); - - const chart = chartState.chart; - - if (!chart) return; - - try { - setWhitespace(chart, scale); - - console.log(`preset: ${preset.id}`); - - const legend = preset.applyPreset({ - chart, - datasets, - preset, - activeResources, - }); - - legendSetter(legend); - } catch (error) { - console.error("chart: render: failed", error); - } -} diff --git a/app/src/scripts/lightweightCharts/chart/types.d.ts b/app/src/scripts/lightweightCharts/chart/types.d.ts index 950f640e7..d6ffd078e 100644 --- a/app/src/scripts/lightweightCharts/chart/types.d.ts +++ b/app/src/scripts/lightweightCharts/chart/types.d.ts @@ -1,8 +1,7 @@ interface PriceSeriesOptions { - halved?: boolean; + placement?: "top" | "bottom"; title?: string; id?: string; - lowerOpacity?: boolean; inverseColors?: boolean; seriesOptions?: DeepPartial<SeriesOptionsCommon>; priceScaleOptions?: DeepPartial<PriceScaleOptions>; diff --git a/app/src/scripts/lightweightCharts/series/creators/candlesticks.ts b/app/src/scripts/lightweightCharts/series/creators/candlesticks.ts index 808085e84..06374fe18 100644 --- a/app/src/scripts/lightweightCharts/series/creators/candlesticks.ts +++ b/app/src/scripts/lightweightCharts/series/creators/candlesticks.ts @@ -2,25 +2,13 @@ import { colors } from "/src/scripts/utils/colors"; export const createCandlesticksSeries = ( chart: IChartApi, - options: PriceSeriesOptions, + options: PriceSeriesOptions = {}, ): [ISeriesApi<"Candlestick">, string[]] => { - const { inverseColors, lowerOpacity } = options; + const { inverseColors } = options; - const upColor = lowerOpacity - ? inverseColors - ? colors.darkLoss - : colors.darkProfit - : inverseColors - ? colors.loss - : colors.profit; + const upColor = inverseColors ? colors.loss : colors.profit; - const downColor = lowerOpacity - ? inverseColors - ? colors.darkProfit - : colors.darkLoss - : inverseColors - ? colors.profit - : colors.loss; + const downColor = inverseColors ? colors.profit : colors.loss; const candlestickSeries = chart.addCandlestickSeries({ baseLineVisible: false, @@ -34,7 +22,6 @@ export const createCandlesticksSeries = ( borderColor: "", borderDownColor: "", borderUpColor: "", - // lastValueVisible: false, ...options.seriesOptions, }); diff --git a/app/src/scripts/lightweightCharts/series/creators/legend.ts b/app/src/scripts/lightweightCharts/series/creators/legend.ts index 81017da56..4a02aebc8 100644 --- a/app/src/scripts/lightweightCharts/series/creators/legend.ts +++ b/app/src/scripts/lightweightCharts/series/creators/legend.ts @@ -8,9 +8,6 @@ import { } from "/src/scripts/utils/urlParams"; import { createRWS } from "/src/solid/rws"; -import { chartState } from "../../chart/state"; -import { setTimeScale } from "../../chart/time"; - export function createSeriesLegend({ id, presetId, @@ -44,15 +41,20 @@ export function createSeriesLegend({ const disabled = createMemo(_disabled || (() => false)); + const drawn = createMemo(() => visible() && !disabled()); + createEffect(() => { - const v = visible(); - const d = disabled(); - series.applyOptions({ - visible: !d && v, + visible: drawn(), }); + }); - setTimeScale(chartState.range); + createEffect(() => { + if (disabled()) { + return; + } + + const v = visible(); if (v !== defaultVisible) { writeURLParam(id, v); @@ -70,6 +72,7 @@ export function createSeriesLegend({ color, visible, disabled, + drawn, url, }; } diff --git a/app/src/scripts/lightweightCharts/series/options/priceScale.ts b/app/src/scripts/lightweightCharts/series/options/priceScale.ts deleted file mode 100644 index 1a622999e..000000000 --- a/app/src/scripts/lightweightCharts/series/options/priceScale.ts +++ /dev/null @@ -1,45 +0,0 @@ -export const resetRightPriceScale = ( - chart: IChartApi, - options?: FullPriceScaleOptions, -) => { - const finalOptions = { - ...options, - scaleMargins: { - ...(options?.halved - ? { - top: 0.5, - bottom: 0.05, - } - : { - top: 0.1, - bottom: 0.1, - }), - ...options?.scaleMargins, - }, - }; - - chart.priceScale("right").applyOptions(finalOptions); - - return finalOptions; -}; - -export const resetLeftPriceScale = ( - chart: IChartApi, - options?: FullPriceScaleOptions, -) => - chart.priceScale("left").applyOptions({ - visible: false, - ...options, - scaleMargins: { - ...(options?.halved - ? { - top: 0.475, - bottom: 0.025, - } - : { - top: 0.25, - bottom: 0.25, - }), - ...options?.scaleMargins, - }, - }); diff --git a/app/src/scripts/lightweightCharts/series/options/types.d.ts b/app/src/scripts/lightweightCharts/series/options/types.d.ts deleted file mode 100644 index 881c38737..000000000 --- a/app/src/scripts/lightweightCharts/series/options/types.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -interface FullPriceScaleOptions extends DeepPartial<PriceScaleOptions> { - halved?: boolean; -} diff --git a/app/src/scripts/presets/addresses/index.ts b/app/src/scripts/presets/addresses/index.ts index ffed6bf08..f29b0027c 100644 --- a/app/src/scripts/presets/addresses/index.ts +++ b/app/src/scripts/presets/addresses/index.ts @@ -4,8 +4,8 @@ import { } from "../../datasets/consts/address"; import { liquidities } from "../../datasets/consts/liquidities"; import { colors } from "../../utils/colors"; +import { applySeriesList, SeriesType } from "../apply"; import { createCohortPresetList } from "../templates/cohort"; -import { applyMultipleSeries, SeriesType } from "../templates/multiple"; export function createPresets({ scale, @@ -22,12 +22,9 @@ export function createPresets({ description: "", icon: IconTablerWallet, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: `Total Non Empty Address`, color: colors.bitcoin, @@ -45,12 +42,9 @@ export function createPresets({ description: "", icon: IconTablerSparkles, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: `New Addresses`, color: colors.white, @@ -67,12 +61,9 @@ export function createPresets({ description: "", icon: IconTablerArchive, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: `Total Addresses Created`, color: colors.bitcoin, @@ -90,12 +81,9 @@ export function createPresets({ description: "", icon: IconTablerTrash, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: `Total Empty Addresses`, color: colors.darkWhite, @@ -191,12 +179,9 @@ export function createAddressCountPreset<Scale extends ResourceScale>({ title: `${name} Address Count`, icon: IconTablerAddressBook, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Address Count", color, diff --git a/app/src/scripts/presets/apply.ts b/app/src/scripts/presets/apply.ts new file mode 100644 index 000000000..1cb8be921 --- /dev/null +++ b/app/src/scripts/presets/apply.ts @@ -0,0 +1,357 @@ +import { createRWS } from "/src/solid/rws"; + +import { createChart } from "../lightweightCharts/chart/create"; +import { applyPriceSeries } from "../lightweightCharts/chart/price"; +import { chartState } from "../lightweightCharts/chart/state"; +import { setWhitespace } from "../lightweightCharts/chart/whitespace"; +import { createAreaSeries } from "../lightweightCharts/series/creators/area"; +import { + createBaseLineSeries, + DEFAULT_BASELINE_COLORS, +} from "../lightweightCharts/series/creators/baseLine"; +import { createHistogramSeries } from "../lightweightCharts/series/creators/histogram"; +import { createSeriesLegend } from "../lightweightCharts/series/creators/legend"; +import { createLineSeries } from "../lightweightCharts/series/creators/line"; +import { debounce } from "../utils/debounce"; +import { stringToId } from "../utils/id"; + +export enum SeriesType { + Normal, + Based, + Area, + Histogram, +} + +type SeriesConfig<Scale extends ResourceScale> = + | { + dataset: AnyDataset<Scale>; + color?: string; + colors?: undefined; + seriesType: SeriesType.Based; + title: string; + options?: BaselineSeriesOptions; + defaultVisible?: boolean; + } + | { + dataset: AnyDataset<Scale>; + color?: string; + colors?: string[]; + seriesType: SeriesType.Histogram; + title: string; + options?: DeepPartialHistogramOptions; + defaultVisible?: boolean; + } + | { + dataset: AnyDataset<Scale>; + color: string; + colors?: undefined; + seriesType?: SeriesType.Normal | SeriesType.Area; + title: string; + options?: DeepPartialLineOptions; + defaultVisible?: boolean; + }; + +export function applySeriesList<Scale extends ResourceScale>({ + parentDiv, + charts: reactiveChartList, + top, + bottom, + preset, + priceScaleOptions, + datasets, + priceDataset, + priceOptions, + activeResources, + legendSetter, +}: { + charts: RWS<IChartApi[]>; + parentDiv: HTMLDivElement; + preset: Preset; + legendSetter: Setter<PresetLegend>; + priceDataset?: AnyDataset<Scale>; + priceOptions?: PriceSeriesOptions; + priceScaleOptions?: DeepPartialPriceScaleOptions; + top?: SeriesConfig<Scale>[]; + bottom?: SeriesConfig<Scale>[]; + datasets: Datasets; + activeResources: Accessor<Set<ResourceDataset<any, any>>>; +}) { + reactiveChartList.set((charts) => { + charts.forEach((chart) => { + chart.remove(); + }); + + return []; + }); + + parentDiv.replaceChildren(); + + const scale = preset.scale; + + const legendList: PresetLegend = []; + + const priceSeriesType = createRWS<"Candlestick" | "Line">("Candlestick"); + + const valuesSkipped = createRWS(0); + + const charts = [top || [], bottom] + .flatMap((list) => (list ? [list] : [])) + .flatMap((seriesConfigList, index, array) => { + if (index !== 0 && seriesConfigList.length === 0) { + return []; + } + + const isAnyArea = seriesConfigList.find( + (config) => config.seriesType === SeriesType.Area, + ); + + const div = document.createElement("div"); + + div.className = "w-full cursor-crosshair min-h-0 border-orange-200/10"; + + parentDiv.appendChild(div); + + const chart = createChart(scale, div, { + ...priceScaleOptions, + ...(isAnyArea + ? { + scaleMargins: { + bottom: 0, + }, + } + : {}), + }); + + chartState.chart = chart; + + if (!chart) { + console.log("chart: undefined"); + return []; + } + + setWhitespace(chart, scale); + + const seriesList: ISeriesApi<any>[] = []; + + const _legendList: PresetLegend = []; + + if (index === 0) { + const price = applyPriceSeries({ + chart, + preset, + seriesType: priceSeriesType, + valuesSkipped, + dataset: + priceDataset || + (datasets[preset.scale as Scale].price as unknown as NonNullable< + typeof priceDataset + >), + activeResources, + options: priceOptions, + }); + + _legendList.push(price.lineLegend, price.ohlcLegend); + + seriesList.push(price.lineLegend.series, price.ohlcLegend.series); + } + + seriesList.push( + ...seriesConfigList + .reverse() + .map( + ({ + dataset, + color, + colors, + seriesType: type, + title, + options, + defaultVisible, + }) => { + let series: ISeriesApi< + "Baseline" | "Line" | "Area" | "Histogram" + >; + + if (type === SeriesType.Based) { + series = createBaseLineSeries(chart, { + color, + ...options, + }); + } else if (type === SeriesType.Area) { + series = createAreaSeries(chart, { + color, + autoscaleInfoProvider: ( + getInfo: () => AutoscaleInfo | null, + ) => { + const info = getInfo(); + if (info) { + info.priceRange.minValue = 0; + } + return info; + }, + ...options, + }); + } else if (type === SeriesType.Histogram) { + series = createHistogramSeries(chart, { + color, + ...options, + }); + } else { + series = createLineSeries(chart, { + color, + ...options, + }); + } + + _legendList.push( + createSeriesLegend({ + id: stringToId(title), + presetId: preset.id, + title, + series, + color: () => colors || color || DEFAULT_BASELINE_COLORS, + defaultVisible, + url: dataset.url, + }), + ); + + createEffect(() => { + series.setData( + dataset?.values() || [], + // computeDrawnSeriesValues(dataset?.values(), valuesSkipped()), + ); + }); + + return series; + }, + ), + ); + + _legendList.forEach((legend) => { + legendList.splice(0, 0, legend); + }); + + return [{ div, chart, seriesList, legendList: _legendList }]; + }); + + createEffect(() => { + const visibleCharts: typeof charts = []; + + charts.forEach((chart) => { + if (chart.legendList.some((legend) => legend.drawn())) { + chart.div.style.border = ""; + visibleCharts.push(chart); + } else { + chart.div.style.height = "0px"; + chart.div.style.border = "none"; + } + }); + + visibleCharts.forEach(({ div, chart }, index) => { + const last = index === visibleCharts.length - 1; + + div.style.height = last ? "100%" : "calc(100% - 62px)"; + div.style.borderBottomWidth = last ? "none" : "1px"; + + chart.timeScale().applyOptions({ + visible: last, + }); + }); + }); + + // const seriesType = createRWS( + // checkIfUpClose(chart, chartState.range) || "Candlestick", + // ); + + function updateVisibleRangeRatio( + chart: IChartApi, + range: LogicalRange | null, + ) { + if (!range) return; + + try { + const width = chart.timeScale().width(); + + const ratio = (range.to - range.from) / width; + + if (ratio <= 0.5) { + priceSeriesType.set("Candlestick"); + } else { + priceSeriesType.set("Line"); + + valuesSkipped.set(Math.floor(ratio / 5)); + } + } catch {} + } + + const debouncedUpdateVisibleRangeRatio = debounce( + updateVisibleRangeRatio, + 50, + ); + + charts.forEach(({ chart }, index) => { + chart.timeScale().subscribeVisibleLogicalRangeChange((timeRange) => { + // Last chart otherwise length of timescale is Infinity + if (index === charts.length - 1) { + debouncedUpdateVisibleRangeRatio(chart, timeRange); + } + + charts.forEach(({ chart: _chart }, _index) => { + if (timeRange && index !== _index) { + _chart.timeScale().setVisibleLogicalRange(timeRange); + } + }); + }); + + chart.subscribeCrosshairMove(({ time, sourceEvent }) => { + // Don't override crosshair position from scroll event + if (time && !sourceEvent) return; + + charts.forEach(({ chart: _chart, seriesList }, _index) => { + const first = seriesList.at(0); + + if (first && index !== _index) { + if (time) { + _chart.setCrosshairPosition(NaN, time, first); + } else { + // No time when mouse goes outside the chart + _chart.clearCrosshairPosition(); + } + } + }); + }); + }); + + legendSetter(legendList); + + reactiveChartList.set(() => charts.map(({ chart }) => chart)); +} + +export function computeDrawnSeriesValues<T>( + values: DatasetValue<T>[] | undefined, + valuesSkipped: number, +) { + values = values || []; + + if (valuesSkipped === 0) { + return values; + } else { + const valuesSkippedPlus1 = valuesSkipped + 1; + + // console.log(_valuesSkippedPlus1); + + let length = Math.floor(values.length / valuesSkippedPlus1); + + // console.log(length); + + const filteredValues = new Array(length); + + for (let i = 0; i < length; i++) { + filteredValues[i] = values[i * valuesSkippedPlus1]; + } + + // console.log(filteredValues.length); + + return filteredValues; + } +} diff --git a/app/src/scripts/presets/blocks/index.ts b/app/src/scripts/presets/blocks/index.ts index bdba20584..016961ea3 100644 --- a/app/src/scripts/presets/blocks/index.ts +++ b/app/src/scripts/presets/blocks/index.ts @@ -1,5 +1,5 @@ import { colors } from "../../utils/colors"; -import { applyMultipleSeries, SeriesType } from "../templates/multiple"; +import { applySeriesList, SeriesType } from "../apply"; export function createPresets() { const scale: ResourceScale = "date"; @@ -14,12 +14,9 @@ export function createPresets() { title: "Block Height", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Height", color: colors.bitcoin, @@ -40,19 +37,15 @@ export function createPresets() { title: "Daily Sum Of Blocks Mined", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Target", color: colors.white, dataset: params.datasets.date.blocks_mined_1d_target, options: { lineStyle: 3, - // lineStyle: LineStyle.LargeDashed, }, }, { @@ -82,19 +75,15 @@ export function createPresets() { title: "Weekly Sum Of Blocks Mined", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Target", color: colors.white, dataset: params.datasets.date.blocks_mined_1w_target, options: { lineStyle: 3, - // lineStyle: LineStyle.LargeDashed, }, }, { @@ -113,18 +102,14 @@ export function createPresets() { title: "Monthly Sum Of Blocks Mined", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Target", color: colors.white, dataset: params.datasets.date.blocks_mined_1m_target, options: { - // lineStyle: LineStyle.LargeDashed, lineStyle: 3, }, }, @@ -144,19 +129,15 @@ export function createPresets() { title: "Yearly Sum Of Blocks Mined", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Target", color: colors.white, dataset: params.datasets.date.blocks_mined_1y_target, options: { lineStyle: 3, - // lineStyle: LineStyle.LargeDashed, }, }, { @@ -175,12 +156,9 @@ export function createPresets() { title: "Total Blocks Mined", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Mined", color: colors.bitcoin, @@ -200,12 +178,9 @@ export function createPresets() { title: "Cumulative Block Size", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Size (MB)", color: colors.darkWhite, diff --git a/app/src/scripts/presets/coinblocks/index.ts b/app/src/scripts/presets/coinblocks/index.ts index 94a64a304..d4c7ee45a 100644 --- a/app/src/scripts/presets/coinblocks/index.ts +++ b/app/src/scripts/presets/coinblocks/index.ts @@ -1,5 +1,5 @@ import { colors } from "../../utils/colors"; -import { applyMultipleSeries, SeriesType } from "../templates/multiple"; +import { applySeriesList, SeriesType } from "../apply"; export function createPresets<Scale extends ResourceScale>({ scale, @@ -19,9 +19,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "All Cointime Prices", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: [ + top: [ { title: "Vaulted Price", color: colors.vaultedness, @@ -61,9 +61,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Active Price", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: [ + top: [ { title: "Active Price", color: colors.liveliness, @@ -85,9 +85,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Vaulted Price", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: [ + top: [ { title: "Vaulted Price", color: colors.vaultedness, @@ -109,9 +109,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "True Market Mean", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: [ + top: [ { title: "True Market Mean", color: colors.trueMarketMeanPrice, @@ -133,9 +133,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Cointime Price", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: [ + top: [ { title: "Cointime", color: colors.cointimePrice, @@ -159,16 +159,14 @@ export function createPresets<Scale extends ResourceScale>({ title: "Cointime Capitalizations", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "Market Cap", - color: colors.white, dataset: params.datasets[scale].market_cap, }, @@ -198,13 +196,12 @@ export function createPresets<Scale extends ResourceScale>({ title: "Thermo Cap", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "Thermo Cap", color: colors.thermoCap, @@ -221,13 +218,12 @@ export function createPresets<Scale extends ResourceScale>({ title: "Investor Cap", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "Investor Cap", color: colors.investorCap, @@ -244,12 +240,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Thermo Cap To Investor Cap Ratio (%)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Ratio", color: colors.bitcoin, @@ -272,12 +265,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "All Coinblocks", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinblocks Created", color: colors.coinblocksCreated, @@ -304,12 +294,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Coinblocks Created", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinblocks Created", color: colors.coinblocksCreated, @@ -326,12 +313,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Coinblocks Destroyed", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinblocks Destroyed", color: colors.coinblocksDestroyed, @@ -348,12 +332,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Coinblocks Stored", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinblocks Stored", color: colors.coinblocksStored, @@ -375,12 +356,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "All Cumulative Coinblocks", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Cumulative Coinblocks Created", color: colors.coinblocksCreated, @@ -410,12 +388,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Cumulative Coinblocks Created", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Cumulative Coinblocks Created", color: colors.coinblocksCreated, @@ -433,12 +408,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Cumulative Coinblocks Destroyed", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Cumulative Coinblocks Destroyed", color: colors.coinblocksDestroyed, @@ -456,12 +428,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Cumulative Coinblocks Stored", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Cumulative Coinblocks Stored", color: colors.coinblocksStored, @@ -484,12 +453,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Liveliness (Activity)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Liveliness", color: colors.liveliness, @@ -506,12 +472,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Vaultedness", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Vaultedness", color: colors.vaultedness, @@ -528,12 +491,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Liveliness V. Vaultedness", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Liveliness", color: colors.liveliness, @@ -555,12 +515,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Activity To Vaultedness Ratio", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Activity To Vaultedness Ratio", color: colors.activityToVaultednessRatio, @@ -578,12 +535,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Concurrent Liveliness - Supply Adjusted Coindays Destroyed", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Concurrent Liveliness 14d Median", color: `${colors.liveliness}66`, @@ -606,12 +560,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Liveliness Incremental Change", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Liveliness Incremental Change", color: colors.darkLiveliness, @@ -641,12 +592,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Vaulted Supply", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Vaulted Supply", color: colors.vaultedness, @@ -663,12 +611,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Active Supply", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Active Supply", color: colors.liveliness, @@ -685,12 +630,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Vaulted V. Active", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Circulating Supply", color: colors.coinblocksCreated, @@ -747,12 +689,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Vaulted Supply Net Change", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Vaulted Supply Net Change", color: colors.vaultedness, @@ -769,12 +708,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Active Supply Net Change", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Active Supply Net Change", color: colors.liveliness, @@ -791,12 +727,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Active VS. Vaulted 90 Day Supply Net Change", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Active Supply Net Change", color: `${colors.liveliness}80`, @@ -919,12 +852,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Cointime Supply In Profit", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Circulating Supply", color: colors.coinblocksCreated, @@ -951,12 +881,9 @@ export function createPresets<Scale extends ResourceScale>({ title: "Cointime Supply In Loss", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Circulating Supply", color: colors.coinblocksCreated, @@ -985,13 +912,12 @@ export function createPresets<Scale extends ResourceScale>({ title: "Cointime-Adjusted Yearly Inflation Rate (%)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "Cointime Adjusted", color: colors.coinblocksCreated, @@ -1015,13 +941,12 @@ export function createPresets<Scale extends ResourceScale>({ title: "Cointime-Adjusted Transactions Velocity", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "Cointime Adjusted", color: colors.coinblocksCreated, diff --git a/app/src/scripts/presets/hodlers/index.ts b/app/src/scripts/presets/hodlers/index.ts index f9115c4c4..1f9224b80 100644 --- a/app/src/scripts/presets/hodlers/index.ts +++ b/app/src/scripts/presets/hodlers/index.ts @@ -6,8 +6,8 @@ import { yearCohorts, } from "../../datasets/consts/age"; import { colors } from "../../utils/colors"; +import { applySeriesList } from "../apply"; import { createCohortPresetFolder } from "../templates/cohort"; -import { applyMultipleSeries } from "../templates/multiple"; export function createPresets({ scale }: { scale: ResourceScale }) { return { @@ -20,12 +20,9 @@ export function createPresets({ scale }: { scale: ResourceScale }) { description: "", icon: IconTablerRipple, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: `24h`, color: colors.up_to_1d, diff --git a/app/src/scripts/presets/index.ts b/app/src/scripts/presets/index.ts index b21988cd8..b6c7d3d6a 100644 --- a/app/src/scripts/presets/index.ts +++ b/app/src/scripts/presets/index.ts @@ -48,22 +48,21 @@ export function createPresets(): Presets { ], } satisfies PartialPresetFolder, { - name: "By Height (Coming soon)", + name: "By Height", tree: [ - // createMarketPresets({ scale: "height", datasets }), - // createMinersPresets("height"), - // createTransactionsPresets("height"), - // ...createCohortPresetList({ - // datasets, - // scale: "height", - // color: colors.bitcoin, - // name: "", - // datasetKey: "", - // title: "", - // }), - // createHodlersPresets({ scale: "height", datasets }), - // createAddressesPresets({ scale: "height", datasets }), - // createCoinblocksPresets({ scale: "height", datasets }), + createMarketPresets({ scale: "height" }), + createMinersPresets("height"), + createTransactionsPresets("height"), + ...createCohortPresetList({ + scale: "height", + color: colors.bitcoin, + name: "", + datasetKey: "", + title: "", + }), + createHodlersPresets({ scale: "height" }), + createAddressesPresets({ scale: "height" }), + createCoinblocksPresets({ scale: "height" }), ], } satisfies PartialPresetFolder, ], diff --git a/app/src/scripts/presets/market/averages/index.ts b/app/src/scripts/presets/market/averages/index.ts index f3f880c9a..647c60922 100644 --- a/app/src/scripts/presets/market/averages/index.ts +++ b/app/src/scripts/presets/market/averages/index.ts @@ -1,7 +1,7 @@ import { averages } from "/src/scripts/datasets/date"; import { colors } from "/src/scripts/utils/colors"; -import { applyMultipleSeries } from "../../templates/multiple"; +import { applySeriesList } from "../../apply"; export function createPresets(): PartialPresetFolder { const scale: ResourceScale = "date"; @@ -15,9 +15,9 @@ export function createPresets(): PartialPresetFolder { name: "All", title: "All Averages", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: averages.map((average) => ({ + top: averages.map((average) => ({ title: average.key.toUpperCase(), color: colors[`_${average.key}`], dataset: params.datasets.date[`price_${average.key}_sma`], @@ -60,9 +60,9 @@ function createPresetFolder({ icon: IconTablerMathAvg, title: `${name} Moving Average`, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: [ + top: [ { title: `SMA`, color, diff --git a/app/src/scripts/presets/market/index.ts b/app/src/scripts/presets/market/index.ts index 88718014e..a6738ebea 100644 --- a/app/src/scripts/presets/market/index.ts +++ b/app/src/scripts/presets/market/index.ts @@ -1,5 +1,5 @@ import { colors } from "../../utils/colors"; -import { applyMultipleSeries } from "../templates/multiple"; +import { applySeriesList } from "../apply"; import { createPresets as createAveragesPresets } from "./averages"; import { createPresets as createIndicatorsPresets } from "./indicators"; import { createPresets as createReturnsPresets } from "./returns"; @@ -14,7 +14,7 @@ export function createPresets({ scale }: { scale: ResourceScale }) { name: "Price", title: "Market Price", applyPreset(params) { - return applyMultipleSeries({ ...params }); + return applySeriesList(params); }, description: "", }, @@ -24,7 +24,7 @@ export function createPresets({ scale }: { scale: ResourceScale }) { name: "Performance", title: "Market Performance", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceOptions: { id: "performance", @@ -43,12 +43,9 @@ export function createPresets({ scale }: { scale: ResourceScale }) { name: "Capitalization", title: "Market Capitalization", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Market Cap.", dataset: params.datasets[scale].market_cap, diff --git a/app/src/scripts/presets/market/returns/index.ts b/app/src/scripts/presets/market/returns/index.ts index 0162ccfdd..1d1c95d15 100644 --- a/app/src/scripts/presets/market/returns/index.ts +++ b/app/src/scripts/presets/market/returns/index.ts @@ -3,7 +3,7 @@ import { totalReturns, } from "/src/scripts/datasets/consts/returns"; -import { applyMultipleSeries, SeriesType } from "../../templates/multiple"; +import { applySeriesList, SeriesType } from "../../apply"; export function createPresets() { return { @@ -57,12 +57,9 @@ function createPreset({ icon: IconTablerReceiptTax, title: `${title} Return`, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: `Return (%)`, seriesType: SeriesType.Based, diff --git a/app/src/scripts/presets/miners/index.ts b/app/src/scripts/presets/miners/index.ts index 2fc83eaf1..58a288f19 100644 --- a/app/src/scripts/presets/miners/index.ts +++ b/app/src/scripts/presets/miners/index.ts @@ -1,5 +1,5 @@ import { colors } from "../../utils/colors"; -import { applyMultipleSeries, SeriesType } from "../templates/multiple"; +import { applySeriesList, SeriesType } from "../apply"; export function createPresets(scale: ResourceScale) { return { @@ -20,12 +20,9 @@ export function createPresets(scale: ResourceScale) { title: "Last Coinbase (In Bitcoin)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Last", color: colors.bitcoin, @@ -42,12 +39,9 @@ export function createPresets(scale: ResourceScale) { title: "Last Coinbase (In Dollars)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Last", color: colors.dollars, @@ -71,12 +65,9 @@ export function createPresets(scale: ResourceScale) { title: "Daily Sum Of Bitcoin Coinbases", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinbases (Bitcoin)", color: colors.bitcoin, @@ -93,12 +84,9 @@ export function createPresets(scale: ResourceScale) { title: "Daily Sum Of Dollar Coinbases", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinbases (Dollars)", color: colors.dollars, @@ -122,12 +110,9 @@ export function createPresets(scale: ResourceScale) { title: "Yearly Sum Of Bitcoin Coinbases", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinbases (Bitcoin)", color: colors.bitcoin, @@ -144,12 +129,9 @@ export function createPresets(scale: ResourceScale) { title: "Yearly Sum Of Dollar Coinbases", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinbases (Dollars)", color: colors.dollars, @@ -174,12 +156,9 @@ export function createPresets(scale: ResourceScale) { title: "Cumulative Bitcoin Coinbases", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinbases (Bitcoin)", color: colors.bitcoin, @@ -197,12 +176,9 @@ export function createPresets(scale: ResourceScale) { title: "Cumulative Dollar Coinbases", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Coinbases (Dollars)", color: colors.dollars, @@ -236,12 +212,10 @@ export function createPresets(scale: ResourceScale) { title: "Last Subsidy (In Bitcoin)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + + bottom: [ { title: "Last", color: colors.bitcoin, @@ -258,12 +232,9 @@ export function createPresets(scale: ResourceScale) { title: "Last Subsidy (In Dollars)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Last", color: colors.dollars, @@ -287,12 +258,9 @@ export function createPresets(scale: ResourceScale) { title: "Daily Sum Of Bitcoin Subsidies", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Subsidies (Bitcoin)", color: colors.bitcoin, @@ -309,12 +277,9 @@ export function createPresets(scale: ResourceScale) { title: "Daily Sum Of Dollar Subsidies", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Subsidies (Dollars)", color: colors.dollars, @@ -338,12 +303,9 @@ export function createPresets(scale: ResourceScale) { title: "Yearly Sum Of Bitcoin Subsidies", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Subsidies (Bitcoin)", color: colors.bitcoin, @@ -360,12 +322,9 @@ export function createPresets(scale: ResourceScale) { title: "Yearly Sum Of Dollar Subsidies", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Subsidies (Dollars)", color: colors.dollars, @@ -390,12 +349,9 @@ export function createPresets(scale: ResourceScale) { title: "Cumulative Bitcoin Subsidies", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Subsidies (Bitcoin)", color: colors.bitcoin, @@ -413,12 +369,9 @@ export function createPresets(scale: ResourceScale) { title: "Cumulative Dollar Subsidies", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Subsidies (Dollars)", color: colors.dollars, @@ -452,12 +405,9 @@ export function createPresets(scale: ResourceScale) { title: "Last Fees (In Bitcoin)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Last", color: colors.bitcoin, @@ -474,12 +424,9 @@ export function createPresets(scale: ResourceScale) { title: "Last Fees (In Dollars)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Last", color: colors.dollars, @@ -503,12 +450,9 @@ export function createPresets(scale: ResourceScale) { title: "Daily Sum Of Bitcoin Fees", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Fees (Bitcoin)", color: colors.bitcoin, @@ -525,12 +469,9 @@ export function createPresets(scale: ResourceScale) { title: "Daily Sum Of Dollar Fees", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Fees (Dollars)", color: colors.dollars, @@ -553,12 +494,9 @@ export function createPresets(scale: ResourceScale) { title: "Yearly Sum Of Bitcoin Fees", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Fees (Bitcoin)", color: colors.bitcoin, @@ -575,12 +513,9 @@ export function createPresets(scale: ResourceScale) { title: "Yearly Sum Of Dollar Fees", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Fees (Dollars)", color: colors.dollars, @@ -604,12 +539,9 @@ export function createPresets(scale: ResourceScale) { title: "Cumulative Bitcoin Fees", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Fees (Bitcoin)", color: colors.bitcoin, @@ -626,12 +558,9 @@ export function createPresets(scale: ResourceScale) { title: "Cumulative Dollar Fees", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Fees (Dollars)", color: colors.dollars, @@ -657,12 +586,9 @@ export function createPresets(scale: ResourceScale) { title: "Subsidy V. Fees", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Subsidy (%)", color: colors.bitcoin, @@ -687,13 +613,12 @@ export function createPresets(scale: ResourceScale) { title: "Puell Multiple", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "Multiple", color: colors.bitcoin, @@ -711,13 +636,12 @@ export function createPresets(scale: ResourceScale) { title: "Hash Rate (EH/s)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "1M SMA", color: colors.momentumYellow, @@ -745,13 +669,12 @@ export function createPresets(scale: ResourceScale) { title: "Hash Ribbon (EH/s)", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "1M SMA", color: colors.profit, @@ -774,12 +697,9 @@ export function createPresets(scale: ResourceScale) { title: "Hash Price", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Price ($/PH/s)", color: colors.dollars, @@ -799,13 +719,12 @@ export function createPresets(scale: ResourceScale) { title: "Difficulty", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "Difficulty", color: colors.bitcoin, @@ -825,15 +744,11 @@ export function createPresets(scale: ResourceScale) { title: "Difficulty Adjustment", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Adjustment (%)", - // color: colors.bitcoin, seriesType: SeriesType.Based, dataset: params.datasets[scale].difficulty_adjustment, }, @@ -851,13 +766,12 @@ export function createPresets(scale: ResourceScale) { title: "Annualized Issuance", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "Issuance", color: colors.bitcoin, @@ -875,13 +789,12 @@ export function createPresets(scale: ResourceScale) { title: "Yearly Inflation Rate", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "Rate (%)", color: colors.bitcoin, diff --git a/app/src/scripts/presets/templates/cohort.ts b/app/src/scripts/presets/templates/cohort.ts index 1d8cca23a..3cf43a640 100644 --- a/app/src/scripts/presets/templates/cohort.ts +++ b/app/src/scripts/presets/templates/cohort.ts @@ -1,6 +1,6 @@ import { percentiles } from "../../datasets/consts/percentiles"; import { colors } from "../../utils/colors"; -import { applyMultipleSeries, SeriesType } from "./multiple"; +import { applySeriesList, SeriesType } from "../apply"; export function createCohortPresetFolder<Scale extends ResourceScale>({ scale, @@ -54,12 +54,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Unspent Transaction Outputs Count`, icon: () => IconTablerTicket, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Count", color, @@ -83,9 +80,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ description: "", icon: () => IconTablerTag, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: [ + top: [ { title: "Realized Price", color, @@ -102,12 +99,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Realized Capitalization`, icon: () => IconTablerPigMoney, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: `${name} Realized Cap.`, color, @@ -136,12 +130,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Realized Capitalization 1 Month Net Change`, icon: () => IconTablerStatusChange, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: `Net Change`, seriesType: SeriesType.Based, @@ -161,12 +152,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Realized Profit`, icon: () => IconTablerCash, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Realized Profit", dataset: @@ -185,12 +173,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Realized Loss`, icon: () => IconTablerCoffin, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Realized Loss", dataset: @@ -209,12 +194,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Realized Profit And Loss`, icon: () => IconTablerArrowsVertical, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Profit", color: colors.profit, @@ -242,12 +224,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Net Realized Profit And Loss`, icon: () => IconTablerScale, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Net PNL", seriesType: SeriesType.Based, @@ -267,12 +246,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Net Realized Profit And Loss Relative To Market Capitalization`, icon: () => IconTablerDivide, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Net", seriesType: SeriesType.Based, @@ -292,12 +268,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Cumulative Realized Profit`, icon: () => IconTablerSum, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Cumulative Realized Profit", color: colors.profit, @@ -318,12 +291,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Cumulative Realized Loss`, icon: () => IconTablerSum, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Cumulative Realized Loss", color: colors.loss, @@ -344,12 +314,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Cumulative Net Realized Profit And Loss`, icon: () => IconTablerSum, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Cumulative Net Realized PNL", seriesType: SeriesType.Based, @@ -369,12 +336,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Cumulative Net Realized Profit And Loss 30 Day Change`, icon: () => IconTablerTimeDuration30, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Cumulative Net Realized PNL 30d Change", dataset: @@ -399,12 +363,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Unrealized Profit`, icon: () => IconTablerMoodDollar, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Profit", dataset: @@ -424,12 +385,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Unrealized Loss`, icon: () => IconTablerMoodSadDizzy, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Loss", dataset: @@ -448,12 +406,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Unrealized Profit And Loss`, icon: () => IconTablerArrowsVertical, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Profit", color: colors.profit, @@ -481,12 +436,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Net Unrealized Profit And Loss`, icon: () => IconTablerScale, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Net Unrealized PNL", dataset: @@ -506,12 +458,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Net Unrealized Profit And Loss Relative To Total Market Capitalization`, icon: () => IconTablerDivide, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Relative Net Unrealized PNL", dataset: @@ -540,12 +489,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ icon: () => IconTablerArrowsCross, description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "In Profit", color: colors.profit, @@ -587,12 +533,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ icon: () => IconTablerSum, description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Supply", color, @@ -609,12 +552,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Supply In Profit`, icon: () => IconTablerTrendingUp, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Supply", color: colors.profit, @@ -635,12 +575,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Supply In Loss`, icon: () => IconTablerTrendingDown, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Supply", color: colors.loss, @@ -667,12 +604,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ icon: () => IconTablerArrowsCross, description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "In Profit", color: colors.profit, @@ -718,12 +652,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Total supply Relative To Circulating Supply`, icon: () => IconTablerSum, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Supply", color, @@ -744,12 +675,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Supply In Profit Relative To Circulating Supply`, icon: () => IconTablerTrendingUp, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Supply", color: colors.profit, @@ -770,12 +698,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Supply In Loss Relative To Circulating Supply`, icon: () => IconTablerTrendingDown, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Supply", seriesType: SeriesType.Area, @@ -801,14 +726,11 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Supply In Profit And Loss Relative To Own Supply`, icon: () => IconTablerArrowsCross, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { - title: "In profit", + title: "In Profit", dataset: params.datasets[scale][ `${datasetPrefix}supply_in_profit_to_own_supply_ratio` @@ -816,7 +738,7 @@ export function createCohortPresetList<Scale extends ResourceScale>({ color: colors.profit, }, { - title: "In loss", + title: "In Loss", color: colors.loss, dataset: params.datasets[scale][ @@ -851,12 +773,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Supply In Profit Relative To Own Supply`, icon: () => IconTablerTrendingUp, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Supply", color: colors.profit, @@ -877,12 +796,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Supply In Loss Relative To Own Supply`, icon: () => IconTablerTrendingDown, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Supply", seriesType: SeriesType.Area, @@ -917,9 +833,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} Average Price Paid - Realized Price`, icon: () => IconTablerMathAvg, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: [ + top: [ { title: "Average", color, @@ -937,9 +853,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} deciles`, icon: () => IconTablerSquareHalf, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: percentiles + top: percentiles .filter(({ value }) => Number(value) % 10 === 0) .map(({ name, key }) => ({ dataset: params.datasets[scale][`${datasetPrefix}${key}`], @@ -957,9 +873,9 @@ export function createCohortPresetList<Scale extends ResourceScale>({ title: `${title} ${percentile.title}`, icon: () => IconTablerSquareHalf, applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - list: [ + top: [ { title: percentile.name, color, diff --git a/app/src/scripts/presets/templates/multiple.ts b/app/src/scripts/presets/templates/multiple.ts deleted file mode 100644 index d5d7cb023..000000000 --- a/app/src/scripts/presets/templates/multiple.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { applyPriceSeries } from "../../lightweightCharts/chart/price"; -import { chartState } from "../../lightweightCharts/chart/state"; -import { setTimeScale } from "../../lightweightCharts/chart/time"; -import { createAreaSeries } from "../../lightweightCharts/series/creators/area"; -import { - createBaseLineSeries, - DEFAULT_BASELINE_COLORS, -} from "../../lightweightCharts/series/creators/baseLine"; -import { createHistogramSeries } from "../../lightweightCharts/series/creators/histogram"; -import { createSeriesLegend } from "../../lightweightCharts/series/creators/legend"; -import { createLineSeries } from "../../lightweightCharts/series/creators/line"; -import { resetRightPriceScale } from "../../lightweightCharts/series/options/priceScale"; -import { stringToId } from "../../utils/id"; - -export enum SeriesType { - Normal, - Based, - Area, - Histogram, -} - -export function applyMultipleSeries< - Scale extends ResourceScale, - DS extends Dataset<Scale> & Partial<ResourceDataset<Scale>>, ->({ - chart, - list = [], - preset, - priceScaleOptions, - datasets, - priceDataset, - priceOptions, - activeResources, -}: { - chart: IChartApi; - preset: Preset; - priceDataset?: DS; - priceOptions?: PriceSeriesOptions; - priceScaleOptions?: FullPriceScaleOptions; - list?: ( - | { - dataset: DS; - color?: string; - colors?: undefined; - seriesType: SeriesType.Based; - title: string; - options?: BaselineSeriesOptions; - defaultVisible?: boolean; - } - | { - dataset: DS; - color?: string; - colors?: string[]; - seriesType: SeriesType.Histogram; - title: string; - options?: DeepPartialHistogramOptions; - defaultVisible?: boolean; - } - | { - dataset: DS; - color: string; - colors?: undefined; - seriesType?: SeriesType.Normal | SeriesType.Area; - title: string; - options?: DeepPartialLineOptions; - defaultVisible?: boolean; - } - )[]; - datasets: Datasets; - activeResources: Accessor<Set<ResourceDataset<any, any>>>; -}): PresetLegend { - const { halved } = priceScaleOptions || {}; - - const price = applyPriceSeries({ - chart, - datasets, - preset, - dataset: priceDataset, - activeResources, - options: { - ...priceOptions, - halved, - }, - }); - - const legendList: PresetLegend = [price.lineLegend, price.ohlcLegend]; - - const isAnyArea = list.find( - (config) => config.seriesType === SeriesType.Area, - ); - - const rightPriceScaleOptions = resetRightPriceScale(chart, { - ...priceScaleOptions, - ...(isAnyArea - ? { - scaleMargins: { - bottom: 0, - }, - } - : {}), - }); - - [...list] - .reverse() - .forEach( - ({ - dataset, - color, - colors, - seriesType: type, - title, - options, - defaultVisible, - }) => { - let series: ISeriesApi<"Baseline" | "Line" | "Area" | "Histogram">; - - if (type === SeriesType.Based) { - series = createBaseLineSeries(chart, { - color, - ...options, - }); - } else if (type === SeriesType.Area) { - series = createAreaSeries(chart, { - color, - autoscaleInfoProvider: (getInfo: () => AutoscaleInfo | null) => { - const info = getInfo(); - if (info) { - info.priceRange.minValue = 0; - } - return info; - }, - ...options, - }); - } else if (type === SeriesType.Histogram) { - series = createHistogramSeries(chart, { - color, - ...options, - }); - } else { - series = createLineSeries(chart, { - color, - ...options, - }); - } - - legendList.splice( - 0, - 0, - createSeriesLegend({ - id: stringToId(title), - presetId: preset.id, - title, - series, - color: () => colors || color || DEFAULT_BASELINE_COLORS, - defaultVisible, - url: dataset.url, - }), - ); - - createEffect(() => { - series.setData(dataset?.values() || []); - - setTimeScale(chartState.range); - }); - }, - ); - - createEffect(() => { - const options = { - scaleMargins: { - top: - price.lineLegend.visible() || price.ohlcLegend.visible() - ? rightPriceScaleOptions.scaleMargins.top - : rightPriceScaleOptions.scaleMargins.bottom, - bottom: rightPriceScaleOptions.scaleMargins.bottom, - }, - }; - - chart.priceScale("right").applyOptions(options); - }); - - return legendList; -} diff --git a/app/src/scripts/presets/transactions/index.ts b/app/src/scripts/presets/transactions/index.ts index 7a4a42db2..55aec464f 100644 --- a/app/src/scripts/presets/transactions/index.ts +++ b/app/src/scripts/presets/transactions/index.ts @@ -1,5 +1,5 @@ import { colors } from "../../utils/colors"; -import { applyMultipleSeries } from "../templates/multiple"; +import { applySeriesList } from "../apply"; export function createPresets(scale: ResourceScale) { return { @@ -12,12 +12,9 @@ export function createPresets(scale: ResourceScale) { title: "Transaction Count", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "1M SMA", color: colors.momentumYellow, @@ -48,12 +45,9 @@ export function createPresets(scale: ResourceScale) { title: "Transaction Volume", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "1M SMA", color: colors.momentumYellow, @@ -80,13 +74,12 @@ export function createPresets(scale: ResourceScale) { title: "Transaction Volume In Dollars", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, priceScaleOptions: { - halved: true, mode: 1, }, - list: [ + bottom: [ { title: "1M SMA", color: colors.lightDollars, @@ -124,12 +117,9 @@ export function createPresets(scale: ResourceScale) { title: "Annualized Transaction Volume", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Volume", color: colors.bitcoin, @@ -147,12 +137,9 @@ export function createPresets(scale: ResourceScale) { title: "Annualized Transaction Volume In Dollars", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Volume", color: colors.dollars, @@ -173,12 +160,9 @@ export function createPresets(scale: ResourceScale) { title: "Transactions Velocity", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "Transactions Velocity", color: colors.bitcoin, @@ -195,12 +179,9 @@ export function createPresets(scale: ResourceScale) { title: "Transactions Per Second", description: "", applyPreset(params) { - return applyMultipleSeries({ + return applySeriesList({ ...params, - priceScaleOptions: { - halved: true, - }, - list: [ + bottom: [ { title: "1M SMA", color: colors.lightBitcoin, diff --git a/app/src/scripts/presets/types.d.ts b/app/src/scripts/presets/types.d.ts index fa47640d1..26d0898dc 100644 --- a/app/src/scripts/presets/types.d.ts +++ b/app/src/scripts/presets/types.d.ts @@ -20,13 +20,13 @@ type FilePath = { }[]; type ApplyPreset = (params: { - chart: IChartApi; + charts: RWS<IChartApi[]>; + parentDiv: HTMLDivElement; datasets: Datasets; preset: Preset; activeResources: Accessor<Set<ResourceDataset<any, any>>>; -}) => ApplyPresetReturn; - -type ApplyPresetReturn = PresetLegend; + legendSetter: Setter<PresetLegend>; +}) => void; interface PartialPresetFolder { name: string; diff --git a/app/src/scripts/utils/locale.ts b/app/src/scripts/utils/locale.ts index 3aa85286a..324d922cc 100644 --- a/app/src/scripts/utils/locale.ts +++ b/app/src/scripts/utils/locale.ts @@ -1,31 +1,52 @@ -export const priceToUSLocale = (price: number, compact = true) => { - const absolutePrice = Math.abs(price); - const lessThan100 = absolutePrice < 100; - const lessThan1000 = absolutePrice < 1_000; - const biggerThanMillion = absolutePrice >= 1_000_000; +const suffices = ["M", "B", "T", "Q"]; - return numberToUSLocale( - price, - lessThan1000 ? (lessThan100 ? 2 : 1) : biggerThanMillion ? 3 : 0, - biggerThanMillion && compact - ? { - notation: "compact", - compactDisplay: "short", - } - : undefined, - ); -}; +export function valueToString(value: number) { + const absoluteValue = Math.abs(value); -export const percentageToUSLocale = (percentage: number) => - numberToUSLocale(percentage, 1); + // value = absoluteValue; -const numberToUSLocale = ( + if (isNaN(value)) { + return ""; + // } else if (value === 0) { + // return "0"; + } else if (absoluteValue < 10) { + return numberToUSLocale(value, 3); + } else if (absoluteValue < 100) { + return numberToUSLocale(value, 2); + } else if (absoluteValue < 1_000) { + return numberToUSLocale(value, 1); + } else if (absoluteValue < 100_000) { + return numberToUSLocale(value, 0); + } else if (absoluteValue < 1_000_000) { + return `${numberToUSLocale(value / 1_000, 1)}K`; + } else if (absoluteValue >= 1_000_000_000_000_000_000) { + return "Inf."; + } + + const log = Math.floor(Math.log10(absoluteValue) - 6); + + const letterIndex = Math.floor(log / 3); + const letter = suffices[letterIndex]; + + const modulused = log % 3; + + if (modulused === 0) { + return `${numberToUSLocale(value / (1_000_000 * 1_000 ** letterIndex), 3)}${letter}`; + } else if (modulused === 1) { + return `${numberToUSLocale(value / (1_000_000 * 1_000 ** letterIndex), 2)}${letter}`; + } else { + return `${numberToUSLocale(value / (1_000_000 * 1_000 ** letterIndex), 1)}${letter}`; + } +} + +function numberToUSLocale( value: number, - digits: number, + digits?: number, options?: Intl.NumberFormatOptions | undefined, -) => - value.toLocaleString("en-us", { +) { + return value.toLocaleString("en-us", { ...options, minimumFractionDigits: digits, maximumFractionDigits: digits, }); +} diff --git a/app/src/scripts/utils/selectableList/index.ts b/app/src/scripts/utils/selectableList/dynamic/index.ts similarity index 81% rename from app/src/scripts/utils/selectableList/index.ts rename to app/src/scripts/utils/selectableList/dynamic/index.ts index e407f1a92..6095a058e 100644 --- a/app/src/scripts/utils/selectableList/index.ts +++ b/app/src/scripts/utils/selectableList/dynamic/index.ts @@ -1,7 +1,7 @@ import { createRWS } from "/src/solid/rws"; -export const createSelectableList = <T, L extends T[] = T[]>( - list: L, +export const createDynamicList = <T, L extends T[] = T[]>( + l: L, parameters?: { selected?: L[number]; selectedIndex?: number | null; @@ -10,10 +10,10 @@ export const createSelectableList = <T, L extends T[] = T[]>( const selected = createRWS<L[number] | null>(null); const selectedIndex = createRWS<number | null>(null); - const selectableList: SelectableList<L[number], L> = { + const list: DynamicList<L[number], L> = { selected, selectedIndex, - list: createRWS(list, { + list: createRWS(l, { equals: false, }), select(s) { @@ -83,10 +83,10 @@ export const createSelectableList = <T, L extends T[] = T[]>( toJSON<TJSON, LJSON extends TJSON[] = TJSON[]>( transform: (value: T) => TJSON, filter?: (value: T) => boolean, - ): JSONSelectableList<TJSON, LJSON> { + ): JSONDynamicList<TJSON, LJSON> { return { version: 1, - selectedIndex: getIndexOfSelectedInSelectableList(this), + selectedIndex: getIndexOfSelectedInDynamicList(this), list: (filter ? this.list().filter(filter) : this.list()).map((value) => transform(value), ) as LJSON, @@ -95,18 +95,18 @@ export const createSelectableList = <T, L extends T[] = T[]>( }; if (parameters?.selected !== undefined) { - selectableList.select(parameters.selected); + list.select(parameters.selected); } else if (parameters?.selectedIndex !== undefined) { - selectableList.selectIndex(parameters.selectedIndex); + list.selectIndex(parameters.selectedIndex); } - return selectableList; + return list; }; -export const createSL = createSelectableList; +export const createDSL = createDynamicList; -export const getIndexOfSelectedInSelectableList = <T, L extends T[] = T[]>( - sl: SelectableList<L[number], L>, +export const getIndexOfSelectedInDynamicList = <T, L extends T[] = T[]>( + sl: DynamicList<L[number], L>, ) => { const selected = sl.selected(); diff --git a/app/src/scripts/utils/selectableList/types.d.ts b/app/src/scripts/utils/selectableList/dynamic/types.d.ts similarity index 85% rename from app/src/scripts/utils/selectableList/types.d.ts rename to app/src/scripts/utils/selectableList/dynamic/types.d.ts index 67bf7dcd3..28174db63 100644 --- a/app/src/scripts/utils/selectableList/types.d.ts +++ b/app/src/scripts/utils/selectableList/dynamic/types.d.ts @@ -2,7 +2,7 @@ // JSON // --- -interface JSONSelectableList<T, L extends T[] = T[]> { +interface JSONDynamicList<T, L extends T[] = T[]> { readonly version: 1; selectedIndex: number | null; readonly list: L; @@ -12,7 +12,7 @@ interface JSONSelectableList<T, L extends T[] = T[]> { // Object // --- -interface SelectableList<T, L extends T[] = T[]> { +interface DynamicList<T, L extends T[] = T[]> { readonly selected: Accessor<T | null>; readonly selectedIndex: Accessor<number | null>; readonly list: RWS<L>; @@ -29,5 +29,5 @@ interface SelectableList<T, L extends T[] = T[]> { readonly toJSON: <TJSON, LJSON extends TJSON[] = TJSON[]>( transform: (value: T) => LJSON[number], filter?: (value: T) => boolean, - ) => JSONSelectableList<TJSON, LJSON>; + ) => JSONDynamicList<TJSON, LJSON>; } diff --git a/app/src/scripts/utils/selectableList/static/index.ts b/app/src/scripts/utils/selectableList/static/index.ts new file mode 100644 index 000000000..7ee5d16c0 --- /dev/null +++ b/app/src/scripts/utils/selectableList/static/index.ts @@ -0,0 +1,140 @@ +import { createRWS } from "/src/solid/rws"; + +import { run } from "../../run"; + +export const createStaticList = <T, L extends T[] = T[]>( + l: L, + parameters: { + selected?: L[number]; + selectedIndex?: number; + saveable?: { + mode: "localStorage" | "URLParams" | "both"; + key: string; + }; + defaultValue?: L[number]; + defaultIndex?: number; + }, +) => { + if ( + !l.length || + (parameters.saveable === undefined && + parameters.selected === undefined && + parameters.selectedIndex === undefined) + ) { + throw Error("not possible"); + } + + const selected = createRWS<L[number]>( + run(() => { + let savedIndex: number | undefined; + + if (parameters.saveable) { + if (parameters.saveable.mode !== "localStorage") { + throw Error("unsupported"); + } + + const savedRaw = localStorage.getItem(parameters.saveable.key); + + if (savedRaw) { + savedIndex = Number(savedRaw); + } + } + + if (parameters.selected) { + const found = l.find((v) => v === parameters.selected); + + if (!found) { + throw Error("unreachable"); + } + + return found; + } else { + return ( + l.at(savedIndex ?? parameters.selectedIndex!) ?? + parameters.defaultValue ?? + l[parameters.defaultIndex || 0] + ); + } + }), + ); + + const selectedIndex = createRWS<number>( + run(() => { + if ( + parameters.selectedIndex !== null && + parameters.selectedIndex !== undefined + ) { + const found = l.at(parameters.selectedIndex); + + if (!found) { + throw Error("unreachable"); + } + + return parameters.selectedIndex; + } else { + return l.indexOf(selected()); + } + }), + ); + + createEffect(() => { + if (parameters.saveable) { + localStorage.setItem(parameters.saveable.key, String(selectedIndex())); + } + }); + + const list: StaticList<L[number], L> = { + selected, + selectedIndex, + list: createRWS(l, { + equals: false, + }), + select(s) { + if (this.selected() !== s) { + batch(() => { + selected.set(() => s); + this.selectIndex(this.list().indexOf(s)); + }); + } + }, + selectIndex(i) { + if (i && (i < 0 || i >= this.list().length)) { + throw new Error( + `SelectableList: selectIndex: ${i} is incorrect ! (has ${ + this.list().length + } elements)`, + ); + } + + if (i !== this.selectedIndex()) { + selectedIndex.set(i); + + const value = this.list().at(i); + + if (value === undefined) { + throw Error("unreachable"); + } + + this.select(value); + } + }, + }; + + if (parameters?.selected !== undefined) { + list.select(parameters.selected); + } else if (parameters?.selectedIndex !== undefined) { + list.selectIndex(parameters.selectedIndex); + } + + return list; +}; + +export const createSL = createStaticList; + +export const getIndexOfSelectedInStaticList = <T, L extends T[] = T[]>( + sl: StaticList<L[number], L>, +) => { + const selected = sl.selected(); + + return selected ? sl.list().indexOf(selected) : null; +}; diff --git a/app/src/scripts/utils/selectableList/static/types.d.ts b/app/src/scripts/utils/selectableList/static/types.d.ts new file mode 100644 index 000000000..55ad8fd6b --- /dev/null +++ b/app/src/scripts/utils/selectableList/static/types.d.ts @@ -0,0 +1,9 @@ +interface StaticList<T, L extends T[] = T[]> { + readonly selected: Accessor<T>; + readonly selectedIndex: Accessor<number>; + readonly list: RWS<L>; + readonly select: <S extends L[number] = L[number]>(s: S) => void; + readonly selectIndex: (index: number) => void; +} + +type SL<T, L extends T[] = T[]> = StaticList<T, L>; diff --git a/app/src/styles.css b/app/src/styles.css index 588722c24..f578e45ca 100644 --- a/app/src/styles.css +++ b/app/src/styles.css @@ -28,9 +28,39 @@ html { } a { - @apply text-orange-300 hover:underline; + @apply text-orange-700 hover:underline dark:text-orange-300; } mark { - @apply bg-transparent p-0 text-orange-400; + @apply bg-transparent p-0 text-orange-600 dark:text-orange-400; +} + +/* Hide scrollbar for Chrome, Safari and Opera */ +.no-scrollbar::-webkit-scrollbar { + display: none; +} + +/* Hide scrollbar for IE, Edge and Firefox */ +.no-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +.border-light { + @apply border-orange-800/25 dark:border-orange-200/25; +} + +.border-lighter { + @apply border-orange-800/[0.125] dark:border-orange-200/[0.125]; +} + +.border-superlight { + @apply border-orange-800/5 dark:border-orange-200/[0.0625]; +} + +.text-high-contrast { + @apply text-orange-950 dark:text-orange-50; +} +.decoration-high-contrast { + @apply decoration-orange-950 dark:decoration-orange-50; } diff --git a/app/src/types/lightweight-charts.d.ts b/app/src/types/lightweight-charts.d.ts index c41d0e8e4..829aa3de6 100644 --- a/app/src/types/lightweight-charts.d.ts +++ b/app/src/types/lightweight-charts.d.ts @@ -63,3 +63,5 @@ type DeepPartialBaselineOptions = DeepPartial< >; type DeepPartialChartOptions = DeepPartial<ChartOptions>; + +type DeepPartialPriceScaleOptions = DeepPartial<PriceScaleOptions>; \ No newline at end of file diff --git a/app/tailwind.config.ts b/app/tailwind.config.ts index bd0f2af2c..6c1e4d727 100644 --- a/app/tailwind.config.ts +++ b/app/tailwind.config.ts @@ -14,7 +14,6 @@ export default { sans: ["Lexend", ...defaultTheme.fontFamily.sans], }, screens: { - md: "720px", "2xl": "1600px", short: { raw: "(max-height: 350px)" }, }, diff --git a/parser/src/datasets/cointime.rs b/parser/src/datasets/cointime.rs index 2f98d8e16..04a0454db 100644 --- a/parser/src/datasets/cointime.rs +++ b/parser/src/datasets/cointime.rs @@ -70,7 +70,7 @@ impl CointimeDataset { active_supply: BiMap::new_bin(1, &f("active_supply")), active_supply_3m_net_change: BiMap::new_bin(1, &f("active_supply_3m_net_change")), active_supply_net_change: BiMap::new_bin(1, &f("active_supply_net_change")), - activity_to_vaultedness_ratio: BiMap::new_bin(1, &f("activity_to_vaultedness_ratio")), + activity_to_vaultedness_ratio: BiMap::new_bin(2, &f("activity_to_vaultedness_ratio")), coinblocks_created: BiMap::new_bin(1, &f("coinblocks_created")), coinblocks_destroyed: BiMap::new_bin(1, &f("coinblocks_destroyed")), coinblocks_stored: BiMap::new_bin(1, &f("coinblocks_stored")), @@ -106,7 +106,7 @@ impl CointimeDataset { producerness: BiMap::new_bin(1, &f("producerness")), thermo_cap: BiMap::new_bin(1, &f("thermo_cap")), thermo_cap_to_investor_cap_ratio: BiMap::new_bin( - 1, + 2, &f("thermo_cap_to_investor_cap_ratio"), ), total_cointime_value_created: BiMap::new_bin(1, &f("total_cointime_value_created")), @@ -215,7 +215,7 @@ impl CointimeDataset { &|liveliness| 1.0 - liveliness, ); - self.activity_to_vaultedness_ratio.multi_insert_divide( + self.activity_to_vaultedness_ratio.multi_insert_percentage( heights, dates, &mut self.liveliness, @@ -332,12 +332,8 @@ impl CointimeDataset { self.investor_cap .multi_insert_subtract(heights, dates, realized_cap, &mut self.thermo_cap); - self.thermo_cap_to_investor_cap_ratio.multi_insert_divide( - heights, - dates, - &mut self.thermo_cap, - &mut self.investor_cap, - ); + self.thermo_cap_to_investor_cap_ratio + .multi_insert_percentage(heights, dates, &mut self.thermo_cap, &mut self.investor_cap); // TODO: // const activeSupplyChangeFromIssuance90dChange = createNetChangeLazyDataset( diff --git a/parser/src/datasets/subs/realized.rs b/parser/src/datasets/subs/realized.rs index 71ed85f5e..529d0e140 100644 --- a/parser/src/datasets/subs/realized.rs +++ b/parser/src/datasets/subs/realized.rs @@ -38,7 +38,7 @@ impl RealizedSubDataset { negative_realized_loss: BiMap::new_bin(2, &f("negative_realized_loss")), net_realized_profit_and_loss: BiMap::new_bin(1, &f("net_realized_profit_and_loss")), net_realized_profit_and_loss_to_market_cap_ratio: BiMap::new_bin( - 1, + 2, &f("net_realized_profit_and_loss_to_market_cap_ratio"), ), cumulative_realized_profit: BiMap::new_bin(1, &f("cumulative_realized_profit")), @@ -107,7 +107,7 @@ impl RealizedSubDataset { ); self.net_realized_profit_and_loss_to_market_cap_ratio - .multi_insert_divide( + .multi_insert_percentage( heights, dates, &mut self.net_realized_profit_and_loss, diff --git a/parser/src/datasets/subs/unrealized.rs b/parser/src/datasets/subs/unrealized.rs index dcfdc8e5c..eb32c81b1 100644 --- a/parser/src/datasets/subs/unrealized.rs +++ b/parser/src/datasets/subs/unrealized.rs @@ -40,7 +40,7 @@ impl UnrealizedSubDataset { negative_unrealized_loss: BiMap::new_bin(1, &f("negative_unrealized_loss")), net_unrealized_profit_and_loss: BiMap::new_bin(1, &f("net_unrealized_profit_and_loss")), net_unrealized_profit_and_loss_to_market_cap_ratio: BiMap::new_bin( - 1, + 2, &f("net_unrealized_profit_and_loss_to_market_cap_ratio"), ), supply_in_profit_to_own_supply_ratio: BiMap::new_bin( @@ -136,7 +136,7 @@ impl UnrealizedSubDataset { ); self.net_unrealized_profit_and_loss_to_market_cap_ratio - .multi_insert_divide( + .multi_insert_percentage( heights, dates, &mut self.net_unrealized_profit_and_loss, diff --git a/parser/src/structs/height_map.rs b/parser/src/structs/height_map.rs index c1c4d62a1..c67dcf638 100644 --- a/parser/src/structs/height_map.rs +++ b/parser/src/structs/height_map.rs @@ -273,20 +273,30 @@ where std::any::type_name::<T>() } - // fn reset(&mut self) -> color_eyre::Result<()> { - // fs::remove_dir(&self.path_all)?; - - // self.initial_last_height = None; - // self.initial_first_unsafe_height = None; - - // self.imported.clear(); - // self.to_insert.clear(); - - // Ok(()) - // } - fn pre_export(&mut self) { - self.to_insert.iter_mut().for_each(|(chunk_start, map)| { + let to_insert = &mut self.to_insert; + + to_insert.iter_mut().for_each(|(chunk_start, map)| { + if let Some((key, _)) = map.first_key_value() { + if *key > 0 && !self.imported.contains_key(chunk_start) { + // Had to copy paste many lines from functions as calling a function from self isn't allowed because of the &mut + + let dir_content = Self::_read_dir(&self.path_all, &self.serialization); + + let path = dir_content.get(chunk_start).unwrap_or_else(|| { + dbg!(&self.path_all, chunk_start, &dir_content); + panic!(); + }); + + let serialized = self + .serialization + .import::<SerializedHeightMap<T>>(path.to_str().unwrap()) + .unwrap(); + + self.imported.insert(*chunk_start, serialized); + } + } + let serialized = self .imported .entry(*chunk_start) @@ -301,7 +311,10 @@ where |(chunk_height, value)| match serialized.map.len().cmp(&chunk_height) { Ordering::Greater => serialized.map[chunk_height] = value, Ordering::Equal => serialized.map.push(value), - Ordering::Less => panic!(), + Ordering::Less => { + dbg!(&self.path_all, &serialized.map, chunk_height, value); + panic!() + } }, ); });