From bcc60cef83f37854b21197241caccb59cf7bd000 Mon Sep 17 00:00:00 2001 From: Yeon Vinzenz Varapragasam Date: Wed, 10 Jul 2024 19:58:06 +0200 Subject: [PATCH] Big Refactoring; Updates; 2D-Matrix; Dashboard; Prepare dnd (#64) * update + prepare dnd * 2D Virtual * Remove defaultProps * fix yarn & eslint madness. * fix storybook * . * add move selected group in matrix functions * dnd elements * auto-generate dummy for gaps * utilize flip parameter for matrix to segments translation * Extend Dashboard with Status and Actions * cleanup * enable dnd for single pixels; update ui * update virtuals segments on save * madness * . * linting * linting * . * . * . --------- Co-authored-by: Yeon Vinzenz Varapragasam --- .eslintrc | 97 --- .gitignore | 1 + .prettierrc | 3 +- .storybook/main.ts | 24 +- .storybook/manager.ts | 10 +- .storybook/preview.tsx | 38 +- .storybook/storyTheme.ts | 8 +- eslint.config.mjs | 103 +++ package.json | 85 ++- src/App.tsx | 10 +- src/api/ledfx.ts | 10 +- src/components/AddButton/AddB.styles.tsx | 5 - src/components/AddButton/AddButton.props.tsx | 1 - src/components/AddButton/AddButton.tsx | 1 - src/components/Bars/BarLeft.tsx | 3 +- src/components/Bars/BarTop.tsx | 1 - src/components/Dialogs/AddDeviceDialog.tsx | 177 ++--- .../Dialogs/AddIntegrationDialog.tsx | 12 +- src/components/Dialogs/AddVirtualDialog.tsx | 12 +- src/components/Dialogs/AddWledDialog.tsx | 1 - src/components/Dialogs/Download.tsx | 1 - src/components/Dialogs/HostManager.tsx | 498 +++++++++----- src/components/Dialogs/IntroDialog.tsx | 496 +++++++++----- src/components/Dialogs/NoHostDialog.tsx | 180 +++-- .../Dialogs/SceneDialogs/EditSceneDialog.tsx | 14 +- src/components/Dialogs/SmartBar.tsx | 21 +- src/components/Dialogs/_AddSegmentDialog.tsx | 3 - src/components/Doc/Doc.tsx | 4 - src/components/Gamepad/Gamepad.props.tsx | 1 - src/components/Gamepad/Gamepad.tsx | 34 +- src/components/Gamepad/GamepadSvg.tsx | 1 - src/components/Gamepad/GamepadSvgPs3.tsx | 267 +++++--- src/components/Gamepad/GamepadSvgPs4.tsx | 183 +++++- src/components/Gamepad/GamepadSvgPs5.tsx | 477 ++++++++++++-- src/components/GlobalActionBar.tsx | 101 +-- .../Icons/BladeIcon/BladeIcon.interface.tsx | 9 - .../Icons/BladeIcon/BladeIcon.stories.tsx | 6 +- src/components/Icons/BladeIcon/BladeIcon.tsx | 7 +- src/components/Icons/FX.tsx | 5 - src/components/Icons/waves.tsx | 1 + .../QLC/DialogAddEventListener.tsx | 133 ++-- .../Integrations/QLC/QLCTriggerTable.tsx | 1 - .../Spotify/SpotifyAuthButton.tsx | 16 +- .../Integrations/Spotify/SpotifyProvider.tsx | 10 +- .../Integrations/Spotify/Widgets/Mp/Mp.tsx | 3 - .../SpotifyWidgetFree/SpotifyWidgetFree.tsx | 1 - .../Widgets/SpotifyWidgetPro/SpControls.tsx | 1 - .../Widgets/SpotifyWidgetPro/SpPlaylist.tsx | 87 ++- .../SpotifyWidgetPro/SpSceneTrigger.tsx | 1 - .../Widgets/SpotifyWidgetPro/SpTrack.tsx | 1 - .../Widgets/SpotifyWidgetPro/SpWidgetPro.tsx | 5 +- .../Integrations/Youtube/YoutubeWidget.tsx | 1 - src/components/MGraph.tsx | 54 +- src/components/MidiInput.tsx | 1 - src/components/NoYet.tsx | 6 +- src/components/OneTimePassword.tsx | 3 +- src/components/PixelGraph.tsx | 210 +++--- src/components/Popover/Popover.stories.tsx | 6 +- src/components/Popover/Popover.tsx | 5 +- .../EffectSchemaForm.props.tsx | 6 - .../EffectSchemaForm.stories.tsx | 8 +- .../EffectsSchemaForm/EffectSchemaForm.tsx | 7 +- .../SchemaForm.examples.stories.tsx | 16 +- .../SchemaForm/SchemaForm.props.tsx | 6 - .../SchemaForm/SchemaForm.stories.tsx | 8 +- .../SchemaForm/SchemaForm/SchemaForm.tsx | 5 +- .../SchemaForm/components/BladeFrame.tsx | 36 +- .../components/Boolean/BladeBoolean.props.tsx | 14 - .../Boolean/BladeBoolean.stories.tsx | 6 +- .../components/Boolean/BladeBoolean.tsx | 9 +- .../DropDown/DropDown.examples.stories.tsx | 6 +- .../components/DropDown/DropDown.props.tsx | 28 - .../components/DropDown/DropDown.stories.tsx | 6 +- .../components/DropDown/DropDown.tsx | 36 +- .../components/DropDown/DropDown.wrapper.tsx | 1 - .../SchemaForm/components/Gif/GifFrame.tsx | 11 +- .../components/Gif/GifFramePicker.tsx | 2 - .../SchemaForm/components/Gif/GifPicker.tsx | 1 - .../GradientPicker/GradientPicker.props.tsx | 12 - .../GradientPicker/GradientPicker.stories.tsx | 6 +- .../GradientPicker/GradientPicker.tsx | 23 +- .../GradientPicker/GradientPicker.wrapper.tsx | 2 - .../components/Number/BladeSlider.props.tsx | 34 - .../components/Number/BladeSlider.stories.tsx | 6 +- .../components/Number/BladeSlider.tsx | 155 +++-- .../components/String/BladeSelect.props.tsx | 16 - .../components/String/BladeSelect.stories.tsx | 6 +- .../components/String/BladeSelect.tsx | 8 +- src/components/Tours/TourHome.tsx | 8 +- src/components/Tours/TourSettings.tsx | 1 - src/components/Webcam/Webcam.tsx | 383 +++++++++++ src/components/Webcam/matrixUtils.ts | 13 + src/components/Webcam/pixelMapper.js | 65 ++ src/components/Webcam/pixelUtils.js | 226 +++++++ src/pages/Device/Cloud/Cloud.tsx | 4 - src/pages/Device/Cloud/CloudComponents.tsx | 2 - src/pages/Device/Device.tsx | 1 - src/pages/Device/Effects.tsx | 3 +- src/pages/Device/Frequencies.tsx | 1 - src/pages/Device/Presets.tsx | 7 +- src/pages/Device/StreamTo.tsx | 1 - .../DeviceCard/DeviceCard.interface.tsx | 28 +- .../Devices/DeviceCard/DeviceCard.stories.tsx | 6 +- src/pages/Devices/DeviceCard/DeviceCard.tsx | 59 +- .../Devices/DeviceCard/DeviceCard.wrapper.tsx | 17 +- src/pages/Devices/Devices.tsx | 1 - .../EditMatrix/Actions/assignPixels.ts | 160 +++++ .../EditMatrix/Actions/hexColor.ts | 16 + .../EditMatrix/Actions/interfaces.ts | 16 + .../Actions/moveSelectedGroupDown.ts | 39 ++ .../Actions/moveSelectedGroupLeft.ts | 26 + .../Actions/moveSelectedGroupRight.ts | 26 + .../EditMatrix/Actions/moveSelectedGroupUp.ts | 40 ++ .../EditMatrix/AssignPixelDialog.tsx | 130 ++++ .../EditVirtuals/EditMatrix/Draggable.tsx | 51 ++ .../EditVirtuals/EditMatrix/Droppable.tsx | 45 ++ .../Devices/EditVirtuals/EditMatrix/M.tsx | 620 ++++++++++++------ .../EditVirtuals/EditMatrix/M.utils.tsx | 41 +- .../EditVirtuals/EditMatrix/MContextMenu.tsx | 67 ++ .../EditVirtuals/EditMatrix/MControls.tsx | 410 +++++++++--- .../EditVirtuals/EditMatrix/MWrapper.tsx | 33 +- .../Devices/EditVirtuals/EditMatrix/Pixel.tsx | 104 +++ .../EditVirtuals/EditMatrix/processMatrix.ts | 112 ++++ .../Devices/EditVirtuals/EditVirtuals.tsx | 6 +- .../Devices/EditVirtuals/PixelSlider.tsx | 15 +- src/pages/Graph/Graph.tsx | 3 +- src/pages/Home/Dashboard.tsx | 6 +- src/pages/Home/DbConfig.tsx | 32 +- src/pages/Home/DbDevices.tsx | 222 +++++-- src/pages/Home/DbGlobalActions.tsx | 2 - src/pages/Home/DbStats.tsx | 53 +- src/pages/Home/Gauge.tsx | 13 +- src/pages/Home/Home.tsx | 5 +- .../IntegrationCard/IntegrationCardQLC.tsx | 16 +- .../QLCplus/QLCScreen/QLCScreen.props.tsx | 44 +- .../Spotify/SpotifyLoginRedirect.tsx | 150 +++-- .../SpotifyScreen/SpotifyScreen.props.tsx | 30 +- .../Spotify/SpotifyScreen/SpotifyScreen.tsx | 9 +- src/pages/Login/LoginRedirect.tsx | 1 - src/pages/Pages.tsx | 5 +- src/pages/Scenes/ScenesPlaylist.tsx | 2 +- src/pages/Settings/AlphaFeatures.tsx | 5 + src/pages/Settings/BetaFeatures.tsx | 1 - src/pages/Settings/SettingsComponents.tsx | 66 +- src/pages/Settings/Webaudio.tsx | 4 +- .../AvatarPicker/AvatarPicker.interface.tsx | 37 -- src/pages/User/AvatarPicker/AvatarPicker.tsx | 49 +- src/pages/User/User.tsx | 4 +- src/reportWebVitals.ts | 16 +- src/service-worker.ts | 3 +- src/serviceWorkerRegistration.ts | 7 +- src/store/api/storeActions.tsx | 62 +- src/store/api/storeColors.tsx | 3 - src/store/api/storeConfig.tsx | 3 - src/store/api/storeDevices.tsx | 3 - src/store/api/storeIntegrations.tsx | 3 - src/store/api/storeIntegrationsSpotify.tsx | 2 - src/store/api/storePresets.tsx | 3 - src/store/api/storeScenes.tsx | 4 +- src/store/api/storeVirtuals.tsx | 3 - src/store/ui/storeCloud.tsx | 1 - src/store/ui/storeDialogs.tsx | 1 - src/store/ui/storeFeatures.tsx | 9 +- src/store/ui/storeGeneral.tsx | 1 - src/store/ui/storeNotifications.tsx | 1 - src/store/ui/storePad.tsx | 1 - src/store/ui/storeQLCActions.tsx | 3 - src/store/ui/storeSpotifyActions.tsx | 2 - src/store/ui/storeTours.tsx | 11 +- src/store/ui/storeUI.tsx | 6 +- src/store/ui/storeUser.tsx | 1 - src/store/ui/storeVideo.tsx | 52 ++ src/store/ui/storeWebAudio.tsx | 1 - src/store/ui/storeYoutube.tsx | 1 - src/store/useStore.ts | 3 +- ...Structure.stories.mdx => AppStructure.mdx} | 11 +- ...Started.stories.mdx => GettingStarted.mdx} | 18 +- src/stories/Guides.mdx | 7 + src/stories/Guides.stories.mdx | 7 - ...roduction.stories.mdx => Introduction.mdx} | 12 +- src/themes/AppThemes.tsx | 26 +- src/utils/ErrorBoundary.tsx | 5 +- src/utils/Websocket.ts | 5 +- src/utils/helpers.ts | 6 +- src/utils/login.ts | 1 - src/utils/spotifyProxies.ts | 6 +- 186 files changed, 5290 insertions(+), 2498 deletions(-) delete mode 100644 .eslintrc create mode 100644 eslint.config.mjs create mode 100644 src/components/Webcam/Webcam.tsx create mode 100644 src/components/Webcam/matrixUtils.ts create mode 100644 src/components/Webcam/pixelMapper.js create mode 100644 src/components/Webcam/pixelUtils.js create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Actions/assignPixels.ts create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Actions/hexColor.ts create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Actions/interfaces.ts create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupDown.ts create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupLeft.ts create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupRight.ts create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupUp.ts create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/AssignPixelDialog.tsx create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Draggable.tsx create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Droppable.tsx create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/MContextMenu.tsx create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/Pixel.tsx create mode 100644 src/pages/Devices/EditVirtuals/EditMatrix/processMatrix.ts create mode 100644 src/store/ui/storeVideo.tsx rename src/stories/{AppStructure.stories.mdx => AppStructure.mdx} (63%) rename src/stories/{GettingStarted.stories.mdx => GettingStarted.mdx} (60%) create mode 100644 src/stories/Guides.mdx delete mode 100644 src/stories/Guides.stories.mdx rename src/stories/{Introduction.stories.mdx => Introduction.mdx} (54%) diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 1918402b..00000000 --- a/.eslintrc +++ /dev/null @@ -1,97 +0,0 @@ -{ - "extends": [ - "airbnb", - "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended" - ], - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint", - "prettier" - ], - "settings": { - "import/parsers": { - "@typescript-eslint/parser": [ - ".ts", - ".tsx" - ] - }, - "import/resolver": { - "typescript": {}, - "node": { - "extensions": [ - ".js", - ".jsx", - ".ts", - ".tsx" - ] - } - }, - "typescript.format.semicolons": "remove", - "javascriptscript.format.semicolons": "remove" - }, - "ignorePatterns": [ - "**/build/*", - "*.js", - "*.jsx" - ], - "rules": { - "quotes": [ - 2, - "single" - ], - "comma-dangle": "off", - "prettier/prettier": [ - "error", - { - "endOfLine": "auto" - } - ], - "react/jsx-filename-extension": [ - 2, - { - "extensions": [ - ".ts", - ".tsx" - ] - } - ], - "import/no-extraneous-dependencies": [ - 2, - { - "devDependencies": [ - "**/test.tsx", - "**/test.ts" - ] - } - ], - "no-nested-ternary": 0, - "import/extensions": 0, - "@typescript-eslint/indent": [ - 2, - 2 - ], - "react/react-in-jsx-scope": "off", - "react/function-component-definition": [ - 0 - ], - "react/jsx-props-no-spreading": 0, - "camelcase": 0, - "react/no-array-index-key": 0, - "@typescript-eslint/no-explicit-any": 0, - "@prettier/trailing-comma": 0, - "no-unused-vars": [ - "error", - { - "argsIgnorePattern": "^_" - } - ], - "@typescript-eslint/no-unused-vars": [ - "error", - { - "argsIgnorePattern": "^_" - } - ], - "jsx-a11y/label-has-associated-control": 0 - } -} \ No newline at end of file diff --git a/.gitignore b/.gitignore index cd5ac078..19209039 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ extraResources/* .yarn_cache/* BladeTools.pfx +storybook.log \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 1ad99177..79b5e458 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,6 @@ { "singleQuote": true, "semi": false, - "trailingComma": "none" + "trailingComma": "none", + "endOfLine": "auto" } \ No newline at end of file diff --git a/.storybook/main.ts b/.storybook/main.ts index 62866d87..c123ce6d 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,14 +1,24 @@ module.exports = { - "stories": ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], - "addons": ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/preset-create-react-app", "@storybook/addon-mdx-gfm"], - "framework": { - name: "@storybook/react-webpack5", + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/preset-create-react-app', + '@storybook/addon-mdx-gfm' + ], + + framework: { + name: '@storybook/react-webpack5', options: {} }, + core: { disableTelemetry: true }, - docs: { - autodocs: true + + docs: {}, + + typescript: { + reactDocgen: 'react-docgen-typescript' } -}; \ No newline at end of file +} diff --git a/.storybook/manager.ts b/.storybook/manager.ts index d5f07e95..529d42ad 100644 --- a/.storybook/manager.ts +++ b/.storybook/manager.ts @@ -1,11 +1,11 @@ -import { addons } from '@storybook/addons'; +import { addons } from '@storybook/addons' // import { themes } from '@storybook/theming'; -import storyTheme from './storyTheme'; +import storyTheme from './storyTheme' addons.setConfig({ theme: storyTheme, toolbar: { - zoom: { hidden: true }, + zoom: { hidden: true } }, - panelPosition: 'right', -}); \ No newline at end of file + panelPosition: 'right' +}) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 369962d6..4232131e 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,36 +1,44 @@ -import React from 'react'; -import { ThemeProvider } from '@mui/styles'; -import { BladeDarkTheme } from '../src/themes/AppThemes'; -import storyTheme from './storyTheme'; +import React from 'react' +import { ThemeProvider } from '@mui/styles' +// import { fn } from '@storybook/test' +import { BladeDarkTheme } from '../src/themes/AppThemes' +import storyTheme from './storyTheme' import './globals.css' export const decorators = [ (Story: any) => ( - + ) -]; +] export const parameters = { options: { storySort: { method: 'alphabetical', - order: [ 'BladeBook', ['Introduction', 'Getting Started', 'App Structure', 'Guides'], 'UI Components',['Default', 'Examples', 'Components',['*', 'Color'] ], 'Api'], - }, + order: [ + 'BladeBook', + ['Introduction', 'Getting Started', 'App Structure', 'Guides'], + 'UI Components', + ['Default', 'Examples', 'Components', ['*', 'Color']], + 'Api' + ] + } }, - actions: { argTypesRegex: '^on[A-Z].*' }, + // actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color|stroke|currentColor)$/i, - date: /Date$/, - }, + date: /Date$/ + } }, docs: { theme: storyTheme, source: { type: 'dynamic', - excludeDecorators: true, - }, - }, -}; + excludeDecorators: true + } + } +} +export const tags = ['autodocs'] diff --git a/.storybook/storyTheme.ts b/.storybook/storyTheme.ts index 155ae42d..fa1f40f9 100644 --- a/.storybook/storyTheme.ts +++ b/.storybook/storyTheme.ts @@ -1,8 +1,10 @@ -import { create } from '@storybook/theming'; +import { create } from '@storybook/theming' export default create({ base: 'dark', + // eslint-disable-next-line prettier/prettier brandTitle: 'Blade\'s Storybook', brandUrl: 'https://yeonv.com', - brandImage: 'https://user-images.githubusercontent.com/28861537/119760144-c5126680-bea9-11eb-991a-c08eedbc5929.png', -}); \ No newline at end of file + brandImage: + 'https://user-images.githubusercontent.com/28861537/119760144-c5126680-bea9-11eb-991a-c08eedbc5929.png' +}) diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..e3efc7ca --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,103 @@ +import typescriptEslint from '@typescript-eslint/eslint-plugin' +import prettier from 'eslint-plugin-prettier' +import tsParser from '@typescript-eslint/parser' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import js from '@eslint/js' +import { FlatCompat } from '@eslint/eslintrc' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}) + +export default [ + { + ignores: ['**/build/*', '**/*.js', '**/*.jsx', 'extraResources/**'] + }, + ...compat.extends( + 'standard', + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended' + ), + { + plugins: { + '@typescript-eslint': typescriptEslint, + 'prettier/prettier': prettier + }, + + languageOptions: { + parser: tsParser + }, + + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'] + }, + + 'import/resolver': { + typescript: {}, + + node: { + extensions: ['.js', '.jsx', '.ts', '.tsx'] + } + }, + + 'typescript.format.semicolons': 'remove', + 'javascriptscript.format.semicolons': 'remove' + }, + files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'], + rules: { + 'no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_' + } + ], + 'react-hooks/rules-of-hooks': 'warn', + 'react-hooks/exhaustive-deps': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_' + } + ], + quotes: [2, 'single'], + 'max-len': [ + 'error', + { + code: 900, + ignoreComments: true, + ignoreUrls: true, + ignoreStrings: true + } + ], + 'object-curly-newline': 'off', + 'comma-dangle': 'off', + 'import/no-named-as-default': 0, + 'import/no-named-as-default-member': 0, + 'import/no-amd': 0, + 'import/no-mutable-exports': 0, + 'import/newline-after-import': 0, + 'no-nested-ternary': 0, + 'import/extensions': 0, + 'import/first': 0, + '@typescript-eslint/indent': [2, 2], + 'react/react-in-jsx-scope': 'off', + 'react/function-component-definition': [0], + 'react/jsx-props-no-spreading': 0, + 'react/require-default-props': 'off', + 'react/prop-types': 'off', + 'no-plusplus': 0, + semi: ['error', 'never'], + camelcase: 0, + 'react/no-array-index-key': 0, + '@typescript-eslint/no-explicit-any': 0, + '@prettier/trailing-comma': 0, + 'jsx-a11y/label-has-associated-control': 0 + } + } +] diff --git a/package.json b/package.json index bd74d681..04804be0 100644 --- a/package.json +++ b/package.json @@ -10,39 +10,51 @@ "node": ">=20" }, "dependencies": { + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/modifiers": "^7.0.0", "@electron/remote": "^2.0.9", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", - "@mui/icons-material": "^5.15.11", - "@mui/material": "^5.15.11", - "@mui/styles": "^5.15.11", - "@mui/x-data-grid": "^6.19.5", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.6.0", + "@mui/icons-material": "^5.15.21", + "@mui/lab": "^5.0.0-alpha.170", + "@mui/material": "^5.15.21", + "@mui/styles": "^5.15.21", + "@mui/x-data-grid": "^7.8.0", + "@storybook/addons": "^7.6.3", "@storybook/react": "^7.6.3", - "@testing-library/react": "^14.2.1", + "@storybook/theming": "^7.6.3", + "@testing-library/react": "^16.0.0", "@types/qs": "^6.9.11", "@types/reactour": "^1.18.5", + "@typescript-eslint/eslint-plugin": "^7.15.0", + "@typescript-eslint/parser": "^7.15.0", + "@webcam/react": "^1.0.1", "axios": "^1.6.7", "base32-decode": "^1.0.0", - "base32-encode": "1", - "chart.js": "4.4.2", + "base32-encode": "1.2.0", + "chart.js": "4.4.3", "color": "^4.2.3", "compare-versions": "^6.1.0", - "conf": "^12.0.0", + "conf": "^13.0.1", "crypto": "^1.0.1", "deep-object-diff": "^1.1.9", "electron-devtools-installer": "^3.2.0", - "electron-is-dev": "^2.0.0", + "electron-is-dev": "2", "electron-squirrel-startup": "^1.0.0", - "electron-store": "^8.1.0", + "electron-store": "8.2.0", "eslint-config-react-app": "7.0.1", - "immer": "10.0.4", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-prettier": "^5.1.3", + "immer": "10.1.1", "is-electron": "^2.2.2", "jwt-decode": "^4.0.0", "notistack": "^3.0.1", - "oauth-pkce": "0.0.6", + "oauth-pkce": "0.0.7", "prop-types": "^15.8.1", "qrcode": "^1.5.3", - "qs": "^6.11.2", + "qs": "^6.12.2", "react": "^18.2.0", "react-chartjs-2": "5.2.0", "react-dom": "^18.2.0", @@ -54,28 +66,29 @@ "react-hotkeys-hook": "^4.5.0", "react-otp-input": "^3.1.1", "react-rnd": "^10.4.1", - "react-router-dom": "^6.22.1", + "react-router-dom": "^6.24.1", "react-scripts": "5.0.1", - "react-zoom-pan-pinch": "^3.4.2", + "react-webcam": "^7.2.0", + "react-zoom-pan-pinch": "^3.6.1", "reactour": "^1.19.2", "sockette": "^2.0.6", "strip-ansi": "7.1.0", "styled-components": "^6.1.8", "ts-deepmerge": "^7.0.0", - "typescript": "^5.3.3", - "universal-cookie": "^6.1.3", + "typescript": "^5.5.3", + "universal-cookie": "^7.1.4", "use-api-polling": "^0.1.4", "use-debounce": "^10.0.0", "use-indexeddb": "^2.0.2", "use-long-press": "^3.2.0", - "web-vitals": "^3.5.2", + "web-vitals": "^4.2.1", "webmidi": "^3.1.8", "workbox-core": "^7.0.0", "workbox-expiration": "^7.0.0", "workbox-precaching": "^7.0.0", "workbox-routing": "^7.0.0", "workbox-strategies": "^7.0.0", - "zustand": "^4.5.1" + "zustand": "^4.5.4" }, "scripts": { "start": "react-scripts start", @@ -87,14 +100,14 @@ "buildhass": "GENERATE_SOURCEMAP=false react-scripts build", "prebuildhass": "node -e \"let pkg=require('./package.json'); pkg.homepage='./'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\"", "postbuildhass": "node -e \"let pkg=require('./package.json'); pkg.homepage='.'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\" && rm -rf build/app && rm -rf build/preload.js && rm -rf build/renderer.js && rm -rf build/electron.js", - "buildgh": "GENERATE_SOURCEMAP=false PUBLIC_URL=/LedFx-Frontend-v2/ react-scripts build", + "buildgh": "GENERATE_SOURCEMAP=false PUBLIC_URL=/LedFx-Frontend-v2/ CI=false react-scripts build", "prebuildgh": "node -e \"let pkg=require('./package.json'); pkg.homepage='/LedFx-Frontend-v2/'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\"", "postbuildgh": "rm -rf build/app && rm -rf build/preload.js && rm -rf build/renderer.js && rm -rf build/electron.js", "test": "react-scripts test", "eject": "react-scripts eject", "predeploy": "yarn buildgh", "deploy": "gh-pages -d build", - "lint": "eslint . --ext .js,.jsx,.ts,.tsx", + "lint": "eslint --fix", "packs": "electron-builder --dir", "dist": "run-script-os", "predist": "node -e \"let pkg=require('./package.json'); pkg.homepage='.'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\"", @@ -119,7 +132,7 @@ "compile-electron:default": "cp ./app/electron.ts ./build/electron.js && cp ./app/preload.ts ./build/preload.js && cp ./app/renderer.ts ./build/renderer.js && cp ./app/serviceWorker.ts ./build/serviceWorker.js", "electron": "wait-on http://127.0.0.1:3000 && electron .", "postinstall": "electron-builder install-app-deps", - "storybook": "storybook dev -p 6006 -s public", + "storybook": "storybook dev", "build-storybook": "storybook build -s public", "prebuild-storybook": "run-script-os", "prebuild-storybook:win32": "set PUBLIC_URL=/LedFx-Frontend-v2/docs", @@ -156,48 +169,48 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@eslint/compat": "^1.1.0", "@storybook/addon-actions": "^7.6.3", "@storybook/addon-essentials": "^7.6.3", "@storybook/addon-links": "^7.6.3", - "@storybook/addon-mdx-gfm": "7.6.3", - "@storybook/addons": "^7.6.3", + "@storybook/addon-mdx-gfm": "^7.6.3", "@storybook/node-logger": "^7.6.3", "@storybook/preset-create-react-app": "^7.6.3", - "@storybook/react-webpack5": "7.6.3", - "@storybook/theming": "^7.6.3", + "@storybook/react-webpack5": "^7.6.3", "@types/babel__template": "^7.4.4", "@types/chart.js": "^2.9.41", "@types/color": "^3.0.6", + "@types/eslint__js": "^8.42.3", "@types/prop-types": "^15.7.11", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", "@types/react-router-dom": "^5.3.3", "@types/styled-components": "^5.1.34", - "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.0.2", "concurrently": "^8.2.2", "cross-env": "^7.0.3", "electron": "^27.1.2", "electron-builder": "^24.9.1", - "eslint": "8.57.0", - "eslint-config-airbnb": "19.0.4", + "eslint": "9.6.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "2.29.1", - "eslint-plugin-json": "^3.1.0", - "eslint-plugin-jsx-a11y": "6.8.0", - "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-json": "^4.0.0", + "eslint-plugin-jsx-a11y": "6.9.0", + "eslint-plugin-n": "^17.9.0", + "eslint-plugin-promise": "^6.4.0", "eslint-plugin-react": "^7.34.0", - "eslint-plugin-react-hooks": "4.6.0", "gh-pages": "^6.1.1", - "prettier": "^3.2.5", + "prettier": "^3.3.2", "react-is": "^18.2.0", "run-script-os": "^1.1.6", - "storybook": "7.6.3", + "storybook": "^7.6.3", "wait-on": "^7.2.0", "webpack": "^5.90.3" }, + "resolutions": { + "string-width": "4.2.3" + }, "build": { "extraResources": [ { diff --git a/src/App.tsx b/src/App.tsx index 083cad83..3a3c945b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,7 +4,7 @@ import { createTheme, ThemeProvider } from '@mui/material/styles' import { SnackbarProvider } from 'notistack' import isElectron from 'is-electron' import { Box, CssBaseline } from '@mui/material' -import Cookies from 'universal-cookie/es6' +import Cookies from 'universal-cookie' import ws, { WsContext, HandleWs } from './utils/Websocket' import useStore from './store/useStore' import useWindowDimensions from './utils/useWindowDimension' @@ -63,17 +63,14 @@ export default function App() { useEffect(() => { initFrontendConfig() - // eslint-disable-next-line no-console + console.info( - // eslint-disable-next-line no-useless-concat '%c Ledfx ' + '%c\n ReactApp by Blade ', 'padding: 10px 40px; color: #ffffff; border-radius: 5px 5px 0 0; background-color: #800000;', 'background: #fff; color: #800000; border-radius: 0 0 5px 5px;padding: 5px 0;' ) if (window.location.pathname.includes('hassio_ingress')) - // eslint-disable-next-line no-console console.info( - // eslint-disable-next-line no-useless-concat '%c HomeAssistant detected ', 'padding: 3px 5px; border-radius: 5px; color: #ffffff; background-color: #038fc7;' ) @@ -91,7 +88,6 @@ export default function App() { setPlatform(parameters[1]) } if (parameters[0] === 'currentdir') { - // eslint-disable-next-line no-console console.log(parameters[1]) } if (parameters[0] === 'protocol') { @@ -131,7 +127,7 @@ export default function App() { if (protoCall !== '') { // showSnackbar('info', `External call: ${protoCall}`) const proto = protoCall.split('/').filter((n) => n) - // eslint-disable-next-line no-console + console.table({ Domain: proto[1], Action: proto[2], diff --git a/src/api/ledfx.ts b/src/api/ledfx.ts index db4cc3dd..810105e0 100644 --- a/src/api/ledfx.ts +++ b/src/api/ledfx.ts @@ -1,20 +1,20 @@ -/* eslint-disable no-param-reassign */ import axios from 'axios' import { produce } from 'immer' import isElectron from 'is-electron' // import { useStore } from '@/store/useStore'; -// eslint-disable-next-line import/no-cycle + import useStore from '../store/useStore' import type { IStore } from '../store/useStore' -// eslint-disable-next-line prettier/prettier -const baseURL = isElectron() ? 'http://localhost:8888' : window.location.href.split('/#')[0].replace(/\/+$/, '') || 'http://localhost:8888'; +const baseURL = isElectron() + ? 'http://localhost:8888' + : window.location.href.split('/#')[0].replace(/\/+$/, '') || + 'http://localhost:8888' const storedURL = window.localStorage.getItem('ledfx-host') const api = axios.create({ baseURL: storedURL || baseURL }) -// eslint-disable-next-line import/prefer-default-export export const Ledfx = async ( path: string, method?: 'GET' | 'PUT' | 'POST' | 'DELETE', diff --git a/src/components/AddButton/AddB.styles.tsx b/src/components/AddButton/AddB.styles.tsx index 97d39068..8c3be5a9 100644 --- a/src/components/AddButton/AddB.styles.tsx +++ b/src/components/AddButton/AddB.styles.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/indent */ import type { Theme } from '@mui/material' import { styled } from '@mui/material/styles' import { Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material' @@ -60,10 +59,6 @@ export const MenuLine = forwardRef( ) } ) -MenuLine.defaultProps = { - icon: , - name: 'MenuItem' -} export const StyledMenu = ({ open, ...props }: StyledMenuProps) => ( ({ +const StyledDialog = styled(Dialog)(({ theme }) => ({ [`& .${classes.wrapper}`]: { minWidth: '200px', padding: '16px 1.2rem 6px 1.2rem', @@ -41,7 +34,7 @@ const StyledDialog = styled(Dialog)(( alignItems: 'center', '@media (max-width: 580px)': { width: '100%', - margin: '0.5rem 0', + margin: '0.5rem 0' }, '& > label': { top: '-0.7rem', @@ -54,56 +47,54 @@ const StyledDialog = styled(Dialog)(( fontSize: '0.9rem', letterSpacing: '0.1rem', backgroundColor: theme.palette.background.paper, - boxSizing: 'border-box', - }, + boxSizing: 'border-box' + } } -})); +})) const AddDeviceDialog = () => { - - - const getDevices = useStore((state) => state.getDevices); - const getVirtuals = useStore((state) => state.getVirtuals); - const addDevice = useStore((state) => state.addDevice); - const updateDevice = useStore((state) => state.updateDevice); - const setAddWled = useStore((state) => state.setAddWLed); - const devices = useStore((state) => state.devices); - const open = useStore((state) => state.dialogs.addDevice?.open || false); - const deviceId = useStore((state) => state.dialogs.addDevice?.edit || false); - const initial = devices[deviceId] || { type: '', config: {} }; + const getDevices = useStore((state) => state.getDevices) + const getVirtuals = useStore((state) => state.getVirtuals) + const addDevice = useStore((state) => state.addDevice) + const updateDevice = useStore((state) => state.updateDevice) + const setAddWled = useStore((state) => state.setAddWLed) + const devices = useStore((state) => state.devices) + const open = useStore((state) => state.dialogs.addDevice?.open || false) + const deviceId = useStore((state) => state.dialogs.addDevice?.edit || false) + const initial = devices[deviceId] || { type: '', config: {} } const setDialogOpenAddDevice = useStore( (state) => state.setDialogOpenAddDevice - ); + ) - const deviceTypes = useStore((state) => state.schemas?.devices); - const showSnackbar = useStore((state) => state.ui.showSnackbar); - const [deviceType, setDeviceType] = useState(''); - const [model, setModel] = useState({}); + const deviceTypes = useStore((state) => state.schemas?.devices) + const showSnackbar = useStore((state) => state.ui.showSnackbar) + const [deviceType, setDeviceType] = useState('') + const [model, setModel] = useState({}) - const currentSchema = deviceType ? deviceTypes[deviceType].schema : {}; + const currentSchema = deviceType ? deviceTypes[deviceType].schema : {} const handleClose = () => { - setDialogOpenAddDevice(false); - }; + setDialogOpenAddDevice(false) + } const handleAddDevice = () => { const cleanedModel = Object.fromEntries( Object.entries(model).filter(([_, v]) => v !== '') - ); - const defaultModel = {} as any; + ) + const defaultModel = {} as any for (const key in currentSchema.properties) { - currentSchema.properties[key].default !== undefined - ? (defaultModel[key] = currentSchema.properties[key].default) - : undefined; + if (currentSchema.properties[key].default !== undefined) { + defaultModel[key] = currentSchema.properties[key].default + } } const valid = currentSchema.required.every((val: string) => Object.keys({ ...defaultModel, ...cleanedModel }).includes(val) - ); + ) if (!valid) { - showSnackbar('warning', 'Please fill in all required fields.'); + showSnackbar('warning', 'Please fill in all required fields.') } else if ( initial.config && Object.keys(initial.config).length === 0 && @@ -111,52 +102,56 @@ const AddDeviceDialog = () => { ) { addDevice({ type: deviceType, - config: { ...defaultModel, ...cleanedModel }, + config: { ...defaultModel, ...cleanedModel } }).then((res: any) => { if (res !== 'failed') { if (deviceType === 'wled') { - const deviceIps = Object.values(devices).map((device: any) => device.config.ip_address) - const newDevices = [] as { name: string, ip_address: string}[] - res.nodes && res.nodes.forEach((node: any) => { - if (node.ip && !deviceIps.includes(node.ip)) { - newDevices.push({ name: node.name, ip_address: node.ip}) - } - }) + const deviceIps = Object.values(devices).map( + (device: any) => device.config.ip_address + ) + const newDevices = [] as { name: string; ip_address: string }[] + res.nodes && + res.nodes.forEach((node: any) => { + if (node.ip && !deviceIps.includes(node.ip)) { + newDevices.push({ name: node.name, ip_address: node.ip }) + } + }) if (newDevices.length > 0) { setAddWled(newDevices) } } - setDialogOpenAddDevice(false); - getDevices(); - getVirtuals(); + setDialogOpenAddDevice(false) + getDevices() + getVirtuals() } - }); + }) } else { // console.log("EDITING"); updateDevice(deviceId, { config: model }).then((res) => { if (res !== 'failed') { - setDialogOpenAddDevice(false); - getDevices(); - getVirtuals(); + setDialogOpenAddDevice(false) + getDevices() + getVirtuals() } - }); + }) } - }; + } const handleTypeChange = (value: string, init = {}) => { - setDeviceType(value); - setModel(init); - }; + setDeviceType(value) + setModel(init) + } const handleModelChange = (config: any) => { - setModel({ ...model, ...config }); - }; + setModel({ ...model, ...config }) + } function filterObject(obj: any, callback: any) { - return Object.fromEntries(Object.entries(obj). - filter(([key, val]) => callback(key, val))); + return Object.fromEntries( + Object.entries(obj).filter(([key, val]) => callback(key, val)) + ) } useEffect(() => { - handleTypeChange(initial.type, initial.config); - }, [initial.type]); + handleTypeChange(initial.type, initial.config) + }, [initial.type, initial.config]) return ( { {model && ( p !== 'icon_name' && p !== 'name') - .reduce((cur, key) => Object.assign(cur, { [key]: currentSchema.properties[key] }), {}) - }} - model={initial.config && + : { + ...currentSchema, + properties: + currentSchema.properties && + Object.keys(currentSchema.properties) + .filter((p) => p !== 'icon_name' && p !== 'name') + .reduce( + (cur, key) => + Object.assign(cur, { + [key]: currentSchema.properties[key] + }), + {} + ) + } + } + model={ + initial.config && Object.keys(initial.config).length === 0 && initial.config?.constructor === Object ? model - : filterObject(model, (p:string,_v: any) => p !== 'icon_name' && p !== 'name')} + : filterObject( + model, + (p: string, _v: any) => p !== 'icon_name' && p !== 'name' + ) + } onModelChange={handleModelChange} hideToggle={!deviceType} /> @@ -233,7 +246,7 @@ const AddDeviceDialog = () => { - ); -}; + ) +} -export default AddDeviceDialog; +export default AddDeviceDialog diff --git a/src/components/Dialogs/AddIntegrationDialog.tsx b/src/components/Dialogs/AddIntegrationDialog.tsx index 7c8dbabc..89b1a993 100644 --- a/src/components/Dialogs/AddIntegrationDialog.tsx +++ b/src/components/Dialogs/AddIntegrationDialog.tsx @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/indent */ -/* eslint-disable no-unused-expressions */ -/* eslint-disable no-restricted-syntax */ -/* eslint-disable guard-for-in */ + import { useState, useEffect } from 'react' import { styled } from '@mui/material/styles' import { @@ -95,9 +93,9 @@ const AddIntegrationDialog = () => { const defaultModel = {} as any for (const key in currentSchema.properties) { - currentSchema.properties[key].default !== undefined - ? (defaultModel[key] = currentSchema.properties[key].default) - : undefined + if (currentSchema.properties[key].default !== undefined) { + defaultModel[key] = currentSchema.properties[key].default + } } const valid = !currentSchema.required @@ -147,7 +145,7 @@ const AddIntegrationDialog = () => { useEffect(() => { handleTypeChange(initial.type, initial.config) - }, [initial.type]) + }, [initial.type, initial.config]) return ( { const defaultModel = {} as any for (const key in currentSchema.properties) { - currentSchema.properties[key].default !== undefined - ? (defaultModel[key] = currentSchema.properties[key].default) - : undefined + if (currentSchema.properties[key].default !== undefined) { + defaultModel[key] = currentSchema.properties[key].default + } } const valid = currentSchema.required.every((val: string) => @@ -116,7 +112,7 @@ const AddVirtualDialog = () => { useEffect(() => { handleModelChange(initial.config) - }, [virtId]) + }, [virtId, initial.config]) return ( <> diff --git a/src/components/Dialogs/AddWledDialog.tsx b/src/components/Dialogs/AddWledDialog.tsx index c5635249..369e0134 100644 --- a/src/components/Dialogs/AddWledDialog.tsx +++ b/src/components/Dialogs/AddWledDialog.tsx @@ -74,7 +74,6 @@ const AddWledDialog = () => { icon_name: 'wled' } - // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars // let icon = 'wled' const promises: any = apiRef.current.getSelectedRows().forEach((row) => { // if row.name is part of a key of nametoicon then return the value else 'wled' diff --git a/src/components/Dialogs/Download.tsx b/src/components/Dialogs/Download.tsx index 49d1a820..df30d82e 100644 --- a/src/components/Dialogs/Download.tsx +++ b/src/components/Dialogs/Download.tsx @@ -9,7 +9,6 @@ export default function LinearProgressWithLabel(props: any) { {`${Math.round( - // eslint-disable-next-line react/destructuring-assignment props.value )}%`} diff --git a/src/components/Dialogs/HostManager.tsx b/src/components/Dialogs/HostManager.tsx index 146c549d..fc11dbc8 100644 --- a/src/components/Dialogs/HostManager.tsx +++ b/src/components/Dialogs/HostManager.tsx @@ -1,5 +1,5 @@ -/* eslint-disable prettier/prettier */ -import { useState, useEffect } from 'react'; +/* eslint-disable @typescript-eslint/indent */ +import { useState, useEffect } from 'react' import { Button, TextField, @@ -19,69 +19,71 @@ import { Stack, ToggleButtonGroup, ToggleButton, - Tooltip, -} from '@mui/material'; -import { Add, CalendarViewDay, Delete, FormatListBulleted, PlayArrow } from '@mui/icons-material'; -import isElectron from 'is-electron'; -import useStore from '../../store/useStore'; -import Instances from './Instances'; -import SceneImage from '../../pages/Scenes/ScenesImage'; + Tooltip +} from '@mui/material' +import { + Add, + CalendarViewDay, + Delete, + FormatListBulleted, + PlayArrow +} from '@mui/icons-material' +import isElectron from 'is-electron' +import useStore from '../../store/useStore' +import Instances from './Instances' +import SceneImage from '../../pages/Scenes/ScenesImage' import useStyles from '../../pages/Scenes/Scenes.styles' export default function HostManager() { const theme = useTheme() const classes = useStyles() - const [instanceVariant] = useState<'buttons' | 'line'>('line'); - const [commonScenes, setCommonScenes] = useState>({}); - const dialogOpen = useStore((state) => state.hostManager || false); - const edit = useStore((state) => state.dialogs.nohost?.edit || false); - const setDialogOpen = useStore((state) => state.setHostManager); - const setDisconnected = useStore((state) => state.setDisconnected); - const coreParams = useStore((state) => state.coreParams); - const coreStatus = useStore((state) => state.coreStatus); - const setHost = useStore((state) => state.setHost); - const storedURL = window.localStorage.getItem('ledfx-host'); + const [instanceVariant] = useState<'buttons' | 'line'>('line') + const [commonScenes, setCommonScenes] = useState>({}) + const dialogOpen = useStore((state) => state.hostManager || false) + const edit = useStore((state) => state.dialogs.nohost?.edit || false) + const setDialogOpen = useStore((state) => state.setHostManager) + const setDisconnected = useStore((state) => state.setDisconnected) + const coreParams = useStore((state) => state.coreParams) + const coreStatus = useStore((state) => state.coreStatus) + const setHost = useStore((state) => state.setHost) + const storedURL = window.localStorage.getItem('ledfx-host') const storedURLs = JSON.parse( window.localStorage.getItem('ledfx-hosts') || JSON.stringify(['http://localhost:8888']) - ); - const [hosts, setHosts] = useState(['http://localhost:8888']); - const [hostvalue, setHostvalue] = useState('http://localhost:8888'); + ) + const [hosts, setHosts] = useState(['http://localhost:8888']) + const [hostvalue, setHostvalue] = useState('http://localhost:8888') const handleClose = () => { - setDialogOpen(false); - }; + setDialogOpen(false) + } - const handleSave = (ho:string, connect?:boolean) => { - if (connect) setHost(ho); + const handleSave = (ho: string, connect?: boolean) => { + if (connect) setHost(ho) if (!hosts.some((h) => h === ho)) { - window.localStorage.setItem( - 'ledfx-hosts', - JSON.stringify([...hosts, ho]) - ); - setHosts([...hosts, ho]); + window.localStorage.setItem('ledfx-hosts', JSON.stringify([...hosts, ho])) + setHosts([...hosts, ho]) } else { - window.localStorage.setItem('ledfx-hosts', JSON.stringify([...hosts])); - setHosts([...hosts]); + window.localStorage.setItem('ledfx-hosts', JSON.stringify([...hosts])) + setHosts([...hosts]) } - setDisconnected(false); - window.location.reload(); - }; + setDisconnected(false) + window.location.reload() + } const handleDelete = (e: any, title: string) => { - e.stopPropagation(); + e.stopPropagation() window.localStorage.setItem( 'ledfx-hosts', JSON.stringify(hosts.filter((h) => h !== title)) - ); - setHosts(hosts.filter((h) => h !== title)); - }; - + ) + setHosts(hosts.filter((h) => h !== title)) + } useEffect(() => { - if (storedURL) setHostvalue(storedURL); - if (storedURLs) setHosts(storedURLs); - }, [storedURL, setHosts]); + if (storedURL) setHostvalue(storedURL) + if (storedURLs) setHosts(storedURLs) + }, [storedURL, setHosts]) useEffect(() => { if (!storedURL) { @@ -89,39 +91,43 @@ export default function HostManager() { isElectron() ? 'http://localhost:8888' : window.location.href.split('/#')[0].replace(/\/+$/, '') - ); + ) window.localStorage.setItem( 'ledfx-host', isElectron() ? 'http://localhost:8888' : window.location.href.split('/#')[0].replace(/\/+$/, '') - ); + ) // eslint-disable-next-line no-self-assign - window.location.href = window.location.href; + window.location.href = window.location.href } - }, []); - const runningCores = Object.keys(coreStatus).filter((h)=>coreStatus[h] === 'running').map((h)=>parseInt(coreParams[h][1], 10) || 8888) - + }, []) + const runningCores = Object.keys(coreStatus) + .filter((h) => coreStatus[h] === 'running') + .map((h) => parseInt(coreParams[h][1], 10) || 8888) useEffect(() => { async function getCommonScenes(rcores: number[]) { try { // Fetch all scenes from each core const allScenes = await Promise.all( - rcores.map(async (h:number) => { - const res = await fetch(`http://localhost:${h}/api/scenes`); - const json = await res.json(); - return json.scenes; + rcores.map(async (h: number) => { + const res = await fetch(`http://localhost:${h}/api/scenes`) + const json = await res.json() + return json.scenes }) - ); + ) // Get the keys of the scenes from each core - const sceneKeys = allScenes.map((scenes) => Object.keys(scenes)); + const sceneKeys = allScenes.map((scenes) => Object.keys(scenes)) // Find the common keys - const commonKeys = sceneKeys.reduce((a, b) => a.filter(c => b.includes(c) && c !== 'blade-scene')); + const commonKeys = sceneKeys.reduce( + (a, b) => a.filter((c) => b.includes(c) && c !== 'blade-scene'), + [] + ) // Prepare an empty object for the final scenes - const finalScenes: Record = {}; + const finalScenes: Record = {} // Iterate over the common keys commonKeys.forEach((key) => { @@ -132,58 +138,57 @@ export default function HostManager() { finalScenes[key] = { name: scenes[key]?.name, scene_image: scenes[key]?.scene_image - }; + } } // If the current scene's image is not "Wallpaper", update it in finalScenes else if (scenes[key]?.scene_image !== 'Wallpaper') { finalScenes[key] = { name: scenes[key]?.name, scene_image: scenes[key]?.scene_image - }; + } } - }); - }); + }) + }) - return finalScenes; - } catch (_e) { - // console.log(_e); - return {}; + return finalScenes + } catch (e) { + console.log(e) + return {} } } - getCommonScenes(runningCores).then((res)=>setCommonScenes(res)); - - - - }, [coreStatus, coreParams]); + getCommonScenes(runningCores).then((res) => setCommonScenes(res)) + }, [coreStatus, coreParams]) // console.log(commonScenes) const activateCommon = async (scene: string) => { try { await Promise.all( - runningCores.map(async (h:number) => { - const res = await fetch(`http://localhost:${h}/api/scenes`, {method: 'PUT', body: JSON.stringify({ - id: scene, - action: 'activate_in', - ms: 0 - })}); - const json = await res.json(); - return json.scenes; + runningCores.map(async (h: number) => { + const res = await fetch(`http://localhost:${h}/api/scenes`, { + method: 'PUT', + body: JSON.stringify({ + id: scene, + action: 'activate_in', + ms: 0 + }) + }) + const json = await res.json() + return json.scenes }) - ); - } - catch (_e) { - // console.log(_e); + ) + } catch (e) { + console.log(e) } } - const [alignment, setAlignment] = useState('cards'); + const [alignment, setAlignment] = useState('cards') const handleChange = ( event: React.MouseEvent, - newAlignment: string, + newAlignment: string ) => { - setAlignment(newAlignment); - }; + setAlignment(newAlignment) + } return (
@@ -193,7 +198,17 @@ export default function HostManager() { aria-labelledby="form-dialog-title" > - LedFx HostManager beta + LedFx HostManager{' '} + + beta + {!edit && ( @@ -202,53 +217,135 @@ export default function HostManager() { )}
- e.key === 'Enter' && setHosts([...hosts,hostvalue])} onChange={(e) => setHostvalue(e.target.value)} /> -
- Known Hosts + Known Hosts
- {hosts.map(h=>
-
- - + {hosts.map((h) => ( +
+
+ + +
-
)} + ))}
- {isElectron() && window.process?.argv.indexOf('integratedCore') !== -1 && (
-
- - Core Instances - -
- {instanceVariant === 'line' && <> - Port - Status - Instance - {/* Config */} - Actions - - - - } + {isElectron() && + window.process?.argv.indexOf('integratedCore') !== -1 && ( +
+
+ + Core Instances + + +
+ {instanceVariant === 'line' && ( + <> + + Port + + Status + + + Instance + + {/* Config */} + + Actions + + + + + + )} - {Object.keys(coreParams).map((h, i)=>parseInt(coreParams[ho][1], 10) || 8888)} variant={instanceVariant} i={i} instance={h} port={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} key={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} />)} - parseInt(coreParams[ho][1], 10) || 8888)}variant={instanceVariant} instance={false} i={Object.keys(coreParams).length + 1} port={`${parseInt(coreParams[`instance${ Object.keys(coreParams).length }`]?.[1] || '8888', 10) + 1}`} /> -
)} + {Object.keys(coreParams).map((h, i) => ( + parseInt(coreParams[ho][1], 10) || 8888 + )} + variant={instanceVariant} + i={i} + instance={h} + port={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} + key={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} + /> + ))} + parseInt(coreParams[ho][1], 10) || 8888 + )} + variant={instanceVariant} + instance={false} + i={Object.keys(coreParams).length + 1} + port={`${parseInt(coreParams[`instance${Object.keys(coreParams).length}`]?.[1] || '8888', 10) + 1}`} + /> +
+ )} -
-
- +
+
+ - Common Scenes alpha + + Common Scenes{' '} + + alpha + + {Object.keys(commonScenes).length > 0 && ( {Object.keys(commonScenes).length > 0 && ( <> - {alignment === 'list' && <> - Scene - Actions - - - } - {Object.keys(commonScenes).map((sc: string) => alignment === 'cards' ? ( - - activateCommon(sc)}> - - - - - {commonScenes[sc].name} - - - - ) : ( + {alignment === 'list' && ( <> - - - + + + Scene - - {commonScenes[sc].name} - - - - + Actions + + + - ) + )} + {Object.keys(commonScenes).map((sc: string) => + alignment === 'cards' ? ( + + activateCommon(sc)} + > + + + + + {commonScenes[sc].name} + + + + ) : ( + <> + + + + + + {commonScenes[sc].name} + + + + + + + ) )} )}
- {/* @@ -349,5 +483,5 @@ export default function HostManager() {
- ); + ) } diff --git a/src/components/Dialogs/IntroDialog.tsx b/src/components/Dialogs/IntroDialog.tsx index 1339e9e1..0c1e02ee 100644 --- a/src/components/Dialogs/IntroDialog.tsx +++ b/src/components/Dialogs/IntroDialog.tsx @@ -1,5 +1,3 @@ -/* eslint-disable prettier/prettier */ -/* eslint-disable @typescript-eslint/indent */ import Dialog from '@mui/material/Dialog' import { useEffect, useState } from 'react' import { @@ -7,14 +5,18 @@ import { DialogContent, Stack, Typography, - useMediaQuery, + useMediaQuery // useTheme, } from '@mui/material' import Box from '@mui/material/Box' import MobileStepper from '@mui/material/MobileStepper' import Button from '@mui/material/Button' -import { CheckCircleOutlineOutlined, ChevronLeft, ChevronRight } from '@mui/icons-material' -import { useNavigate } from 'react-router-dom'; +import { + CheckCircleOutlineOutlined, + ChevronLeft, + ChevronRight +} from '@mui/icons-material' +import { useNavigate } from 'react-router-dom' import useStore from '../../store/useStore' import logoCircle from '../../icons/png/128x128.png' import banner from '../../icons/png/banner.png' @@ -26,7 +28,11 @@ import BladeSchemaForm from '../SchemaForm/SchemaForm/SchemaForm' import BladeIcon from '../Icons/BladeIcon/BladeIcon' import { SettingsRow } from '../../pages/Settings/SettingsComponents' -export default function IntroDialog({ handleScan, scanning, setScanning }: any) { +export default function IntroDialog({ + handleScan, + scanning, + setScanning +}: any) { const navigate = useNavigate() const intro = useStore((state) => state.intro) const devices = useStore((state) => state.devices) @@ -56,10 +62,9 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) setActiveStep((prevActiveStep) => prevActiveStep - 1) } - const graphsMulti = useStore((state) => state.graphsMulti) const assistant = useStore((state) => state.assistant) - const setAssistant= useStore((state) => state.setAssistant) + const setAssistant = useStore((state) => state.setAssistant) const toggleGraphsMulti = useStore((state) => state.toggleGraphsMulti) const getSystemConfig = useStore((state) => state.getSystemConfig) const setSystemConfig = useStore((state) => state.setSystemConfig) @@ -75,8 +80,8 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) properties: { audio_device: schem?.properties?.audio_device || {}, delay_ms: schem?.properties?.delay_ms || {}, - min_volume: schem?.properties?.min_volume || {}, - }, + min_volume: schem?.properties?.min_volume || {} + } } const model = useStore((state) => state?.config?.audio) @@ -95,7 +100,7 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) action_left: () => { handleClose() }, - action_right: handleNext, + action_right: handleNext }, { key: 'gotWled', @@ -104,7 +109,7 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) label_left: 'No', label_right: 'Yes', action_left: handleNext, - action_right: handleNext, + action_right: handleNext }, { key: 'bladeScene', @@ -113,7 +118,7 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) label_left: 'Skip Blade Scene', label_right: handleNext()} />, action_left: handleNext, - action_right: handleNext, + action_right: handleNext }, { key: 'audio', @@ -122,7 +127,7 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) label_left: 'Apply', label_right: 'Apply', action_left: (): any => false, - action_right: handleNext, + action_right: handleNext }, { key: 'tour', @@ -136,8 +141,8 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) action_right: () => { setTourOpen('home', true) handleClose() - }, - }, + } + } ] as any) useEffect(() => { @@ -150,7 +155,7 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) action_left: () => { handleClose() }, - action_right: handleNext, + action_right: handleNext }, { key: 'gotWled', @@ -174,7 +179,7 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) if (assistant.wled) setScanning(0) if (assistant.wled) handleScan() handleNext() - }, + } }, // s.gotWled === 'right' && { // key: 'wledSegs', @@ -199,10 +204,10 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) label_left: 'Re-Scan', label_right: 'Continue', action_left: handleBack, - action_right: ()=> { + action_right: () => { setScanning(-1) handleNext() - }, + } }, { key: 'bladeScene', @@ -211,7 +216,7 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) label_left: 'Skip Blade Scene', label_right: handleNext()} />, action_left: handleNext, - action_right: handleNext, + action_right: handleNext }, { key: 'audio', @@ -225,7 +230,7 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) navigate('/Devices') } handleNext() - }, + } }, { key: 'tour', @@ -240,21 +245,19 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) action_right: () => { setTourOpen('home', true) handleClose() - }, - }, + } + } ].filter((n: any) => n !== false) setSteps(ste) }, [s, graphsMulti, assistant]) - - return ( @@ -264,27 +267,67 @@ export default function IntroDialog({ handleScan, scanning, setScanning }: any) display: 'flex', alignItems: 'center', justifyContent: 'space-around', - flexDirection: 'column', + flexDirection: 'column' }} > - {!steps[activeStep].icon || - steps[activeStep].icon === 'wled' - ? - logo-circle - {activeStep === 0 ? } label="Free" variant='filled' />} label="OpenSource" variant='filled' />} label="CrossPlatform" variant='filled' /> : null} - - : + logo-circle - } + {activeStep === 0 ? ( + + } + label="Free" + variant="filled" + /> + } + label="OpenSource" + variant="filled" + /> + } + label="CrossPlatform" + variant="filled" + /> + + ) : null} +
+ ) : ( + + )}
- {steps[activeStep].key === 'wledScanning' - ? <>New Devices found:
- - - :steps[activeStep].title} + {steps[activeStep].key === 'wledScanning' ? ( + <> + New Devices found: +
+ + ) : ( + steps[activeStep].title + )}
- {steps[activeStep].key === 'wledScanning' - && - - {devices && Object.keys(devices)?.length}WLEDs - {openRgbDevices.length > 1 && <>
{openRgbDevices.length}OpenRGB Devices} - {launchpadDevice !== '' && <>
1Launchpad} -
} + {steps[activeStep].key === 'wledScanning' && ( + + + {devices && Object.keys(devices)?.length} + + WLEDs + {openRgbDevices.length > 1 && ( + <> +
+ + {openRgbDevices.length} + + OpenRGB Devices + + )} + {launchpadDevice !== '' && ( + <> +
+ + 1 + + Launchpad + + )} +
+ )}
- {steps[activeStep].key === 'audio' &&
- {schema && ( - { - setSystemConfig({ - audio: e, - }).then(() => getSystemConfig()) - }} - /> - )} - - - toggleGraphsMulti()} - style={{ fontSize: 16, paddingLeft: '0.25rem'}} - direct - /> - setFeatures('waves', !features.waves)} - style={{ fontSize: 16, paddingLeft: '0.25rem'}} - direct - /> - setFeatures('scenetables', !features.scenetables)} - style={{ fontSize: 16, paddingLeft: '0.25rem'}} - direct - /> - setFeatures('scenechips', !features.scenechips)} - style={{ fontSize: 16, paddingLeft: '0.25rem'}} - direct - /> - -
} - {steps[activeStep].key === 'gotWled' &&
- - - - wled - setAssistant('wled', !assistant.wled)} - style={{ fontSize: 16, paddingLeft: '0.75rem'}} - direct - /> - - - wled - setAssistant('wledSegments', !assistant.wledSegments)} - style={{ fontSize: 16, paddingLeft: '0.75rem'}} - direct - /> - - - openrgb - setAssistant('openRgb', !assistant.openRgb)} - style={{ fontSize: 16, paddingLeft: '0.75rem'}} - direct - /> - - - wled - setAssistant('launchpad', !assistant.launchpad)} - style={{ fontSize: 16, paddingLeft: '0.75rem'}} - direct - /> - - - - - -
} - - {steps[activeStep].label_left && } + + + + wled + setAssistant('wled', !assistant.wled)} + style={{ fontSize: 16, paddingLeft: '0.75rem' }} + direct + /> + + + wled + + setAssistant('wledSegments', !assistant.wledSegments) + } + style={{ fontSize: 16, paddingLeft: '0.75rem' }} + direct + /> + + + openrgb + setAssistant('openRgb', !assistant.openRgb)} + style={{ fontSize: 16, paddingLeft: '0.75rem' }} + direct + /> + + + wled + + setAssistant('launchpad', !assistant.launchpad) + } + style={{ fontSize: 16, paddingLeft: '0.75rem' }} + direct + /> + + +
+ )} + + {steps[activeStep].label_left && ( + + )} {typeof steps[activeStep].label_right === 'string' ? (
- Known Hosts + Known Hosts
- {hosts.map(h=>
-
- - + {hosts.map((h) => ( +
+
+ + +
-
)} + ))}
- {isElectron() && window.process?.argv.indexOf('integratedCore') !== -1 && (
-
- - Core Instances - -
- {/* {instanceVariant === 'line' && <> + {cc && ( +
+
+ + Core Instances + + +
+ {/* {instanceVariant === 'line' && <> Port Status Instance @@ -144,14 +168,28 @@ export default function NoHostDialog() { } */} - {/* {Object.keys(coreParams).map((h, i)=>parseInt(coreParams[ho][1], 10) || 8888)} variant={instanceVariant} i={i} instance={h} port={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} key={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} />)} - parseInt(coreParams[ho][1], 10) || 8888)}variant={instanceVariant} instance={false} i={Object.keys(coreParams).length + 1} port={`${parseInt(coreParams[`instance${ Object.keys(coreParams).length }`]?.[1] || '8888', 10) + 1}`} /> */} -
)} + {/* {Object.keys(coreParams).map((h, i)=>parseInt(coreParams[ho][1], 10) || 8888)} variant={instanceVariant} i={i} instance={h} port={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} key={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} />)} + parseInt(coreParams[ho][1], 10) || 8888)} + variant={instanceVariant} instance={false} i={Object.keys(coreParams).length + 1} + port={`${parseInt(coreParams[`instance${ Object.keys(coreParams).length }`]?.[1] || '8888', 10) + 1}`} /> */} +
+ )} - +
- setInstanceVariant(instanceVariant === 'line' ? 'buttons' : 'line')} /> - Show as list + + setInstanceVariant( + instanceVariant === 'line' ? 'buttons' : 'line' + ) + } + /> + + Show as list +
- ); + ) } diff --git a/src/components/Dialogs/SceneDialogs/EditSceneDialog.tsx b/src/components/Dialogs/SceneDialogs/EditSceneDialog.tsx index c2503755..5bb9f52b 100644 --- a/src/components/Dialogs/SceneDialogs/EditSceneDialog.tsx +++ b/src/components/Dialogs/SceneDialogs/EditSceneDialog.tsx @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/indent */ -/* eslint-disable react/jsx-no-useless-fragment */ import { useCallback, useEffect, useState } from 'react' import { AppBar, @@ -232,7 +231,6 @@ const EditSceneDialog = () => { WebMidi.enable({ callback(err: Error) { if (err) { - // eslint-disable-next-line no-console console.error('WebMidi could not be enabled:', err) } else { // Get all input devices @@ -543,11 +541,11 @@ const EditSceneDialog = () => { label="Image Type" variant="outlined" value={ - image.startsWith('image:file:///') + image?.startsWith('image:file:///') ? 'image:file:///' - : image.startsWith('image:https://') + : image?.startsWith('image:https://') ? 'image:https://' - : image.startsWith('mdi:') + : image?.startsWith('mdi:') ? 'mdi:' : '' } @@ -575,9 +573,9 @@ const EditSceneDialog = () => { }} type="text" value={image - .replace('image:file:///', '') - .replace('image:https://', '') - .replace('mdi:', '')} + ?.replace('image:file:///', '') + ?.replace('image:https://', '') + ?.replace('mdi:', '')} onChange={(e) => setImage(e.target.value)} fullWidth /> diff --git a/src/components/Dialogs/SmartBar.tsx b/src/components/Dialogs/SmartBar.tsx index 4e9aa49e..3062f873 100644 --- a/src/components/Dialogs/SmartBar.tsx +++ b/src/components/Dialogs/SmartBar.tsx @@ -68,7 +68,6 @@ const Bar = ({ handleClose, direct, maxWidth = 500, inputRef }: any) => { }} onInputChange={(event, value) => { if (value === 'HackedByBlade!') { - // eslint-disable-next-line no-alert alert('DevMode activated!') setFeatures('dev', true) } @@ -216,7 +215,6 @@ const Bar = ({ handleClose, direct, maxWidth = 500, inputRef }: any) => {
)} - // eslint-disable-next-line react/no-unstable-nested-components PaperComponent={({ children }) => ( { style={{ borderRadius: '50%' }} label="SmartBar" placeholder="Jump to device / Activate scene" - // eslint-disable-next-line react/jsx-no-duplicate-props inputProps={{ ...params.inputProps, autoComplete: 'off' // disable autocomplete and autofill @@ -260,11 +257,11 @@ const Bar = ({ handleClose, direct, maxWidth = 500, inputRef }: any) => { } const SmartBar = ({ - open, - setOpen, - direct, - maxWidth, - inputRef + open = false, + setOpen = undefined, + direct = false, + maxWidth = 500, + inputRef = undefined }: { open?: boolean setOpen?: any @@ -299,12 +296,4 @@ const SmartBar = ({ ) } -SmartBar.defaultProps = { - open: false, - setOpen: undefined, - direct: false, - maxWidth: 500, - inputRef: undefined -} - export default SmartBar diff --git a/src/components/Dialogs/_AddSegmentDialog.tsx b/src/components/Dialogs/_AddSegmentDialog.tsx index 6df6cc43..64ffdca9 100644 --- a/src/components/Dialogs/_AddSegmentDialog.tsx +++ b/src/components/Dialogs/_AddSegmentDialog.tsx @@ -1,6 +1,3 @@ -/* eslint-disable react/forbid-prop-types */ -/* eslint-disable react/require-default-props */ -/* eslint-disable react/destructuring-assignment */ import React from 'react' import { styled } from '@mui/material/styles' import PropTypes from 'prop-types' diff --git a/src/components/Doc/Doc.tsx b/src/components/Doc/Doc.tsx index 701f2a9a..da9a6ddf 100644 --- a/src/components/Doc/Doc.tsx +++ b/src/components/Doc/Doc.tsx @@ -1,7 +1,3 @@ -/* eslint-disable @typescript-eslint/indent */ -/* eslint-disable react/require-default-props */ -/* eslint-disable @typescript-eslint/no-empty-function */ -/* eslint-disable import/no-unresolved */ import React from 'react' import Button from '@mui/material/Button' import Dialog from '@mui/material/Dialog' diff --git a/src/components/Gamepad/Gamepad.props.tsx b/src/components/Gamepad/Gamepad.props.tsx index 3733a7af..d7e5dc6e 100644 --- a/src/components/Gamepad/Gamepad.props.tsx +++ b/src/components/Gamepad/Gamepad.props.tsx @@ -2,7 +2,6 @@ import { Box } from '@mui/material' import type { ReactNode } from 'react' export interface TabPanelProps { - // eslint-disable-next-line react/require-default-props children?: ReactNode index: number value: number diff --git a/src/components/Gamepad/Gamepad.tsx b/src/components/Gamepad/Gamepad.tsx index 1e13e491..bfb0cb66 100644 --- a/src/components/Gamepad/Gamepad.tsx +++ b/src/components/Gamepad/Gamepad.tsx @@ -79,8 +79,7 @@ const Gamepad = ({ setScene, bottom }: any) => { setScanning(0) scanForDevices() .then(async () => { - // eslint-disable-next-line no-plusplus - for (let sec = 1; sec <= 30; sec++) { + for (let sec = 1; sec <= 30; sec += 1) { if (scanning === -1) break sleep(1000).then(() => { getDevices() @@ -105,7 +104,6 @@ const Gamepad = ({ setScene, bottom }: any) => { (k: any) => g[k]?.buttons[8].pressed && g[k]?.buttons[9].pressed ) ) { - // eslint-disable-next-line no-alert alert('DevMode activated!') setFeatures('dev', true) } else if ( @@ -127,8 +125,8 @@ const Gamepad = ({ setScene, bottom }: any) => { useEffect(() => { if (!blocked) { const m = [pad0, pad1, pad2, pad3] - m.map((pad: any) => { - return pad?.buttons.map((b: any, i: number) => { + m.map((pad: any) => + pad?.buttons.map((b: any, i: number) => { const test = b.pressed && b.value === 1 && @@ -218,7 +216,7 @@ const Gamepad = ({ setScene, bottom }: any) => { } return null }) - }) + ) } }, [pad0, pad1, pad2, pad3]) @@ -389,19 +387,17 @@ const Gamepad = ({ setScene, bottom }: any) => { spacing={1} sx={{ mt: 2, minWidth: 300 }} > - {pad?.buttons.map((b: any, i: number) => { - return ( - - ) - })} + {pad?.buttons.map((b: any, i: number) => ( + + ))} ) : ( diff --git a/src/components/Gamepad/GamepadSvg.tsx b/src/components/Gamepad/GamepadSvg.tsx index 0b0eae26..6ae05988 100644 --- a/src/components/Gamepad/GamepadSvg.tsx +++ b/src/components/Gamepad/GamepadSvg.tsx @@ -76,7 +76,6 @@ const GamepadSvg = ({ - - @@ -116,65 +149,103 @@ const GamepadSvgPs3 = ({ /> - {type === 'ps3' && <> - } + {type === 'ps3' && ( + <> + + + + )} - {type === 'ps3' && } + {type === 'ps3' && ( + + )} 0.05 || - Math.abs(pad.axes[1]) > 0.05 - ? `${Color(theme.palette.primary.main).alpha( - Math.abs(pad.axes[0]) + Math.abs(pad.axes[1]) - )}` - : type === 'ps3' ? (pad.buttons[10].pressed ? theme.palette.primary.main : stroke) : 'transparent' + Math.abs(pad.axes[0]) > 0.05 || Math.abs(pad.axes[1]) > 0.05 + ? `${Color(theme.palette.primary.main).alpha(Math.abs(pad.axes[0]) + Math.abs(pad.axes[1]))}` + : type === 'ps3' + ? pad.buttons[10].pressed + ? theme.palette.primary.main + : stroke + : 'transparent' } cx={200 + pad.axes[0] * 20} cy={324 + pad.axes[1] * 20} @@ -271,12 +389,13 @@ const GamepadSvgPs3 = ({ 0.05 || - Math.abs(pad.axes[3]) > 0.05 - ? `${Color(theme.palette.primary.main).alpha( - Math.abs(pad.axes[2]) + Math.abs(pad.axes[3]) - )}` - : type === 'ps3' ? (pad.buttons[11].pressed ? theme.palette.primary.main : stroke) : 'transparent' + Math.abs(pad.axes[2]) > 0.05 || Math.abs(pad.axes[3]) > 0.05 + ? `${Color(theme.palette.primary.main).alpha(Math.abs(pad.axes[2]) + Math.abs(pad.axes[3]))}` + : type === 'ps3' + ? pad.buttons[11].pressed + ? theme.palette.primary.main + : stroke + : 'transparent' } cx={381 + pad.axes[2] * 20} cy={324 + pad.axes[3] * 20} @@ -308,14 +427,16 @@ const GamepadSvgPs3 = ({ stroke={cover} strokeWidth={strokeWidth2} /> - {type !== 'ps3' && } + {type !== 'ps3' && ( + + )} { const theme = useTheme() return ( - + - - - - - 0.05 || - Math.abs(pad.axes[1]) > 0.05 - ? `${Color(theme.palette.primary.main).alpha( - Math.abs(pad.axes[0]) + Math.abs(pad.axes[1]) - )}` - : 'transparent'} style={{ transform: `translateX(${pad.axes[0] * 10}px) translateY(${pad.axes[1] * 10}px)` }} stroke={pad.buttons[10]?.pressed ? theme.palette.primary.main : stroke} strokeWidth={strokeWidth} d="M190.892,326.654c-15.024,0-27.252,12.228-27.252,27.258c0,15.025,12.228,27.253,27.252,27.253 c15.031,0,27.258-12.228,27.258-27.253C218.15,338.882,205.923,326.654,190.892,326.654z"/> - 0.05 || - Math.abs(pad.axes[3]) > 0.05 - ? `${Color(theme.palette.primary.main).alpha( - Math.abs(pad.axes[2]) + Math.abs(pad.axes[3]) - )}` - : 'transparent' - } style={{ transform: `translateX(${pad.axes[2] * 10}px) translateY(${pad.axes[3] * 10}px)` }} stroke={pad.buttons[11]?.pressed ? theme.palette.primary.main : stroke} strokeWidth={strokeWidth} d="M378.482,326.654c-15.031,0-27.258,12.228-27.258,27.258c0,15.025,12.227,27.253,27.258,27.253 c15.025,0,27.252-12.228,27.252-27.253C405.734,338.882,393.508,326.654,378.482,326.654z"/> - - - - - - - - - + + + + + 0.05 || Math.abs(pad.axes[1]) > 0.05 + ? `${Color(theme.palette.primary.main).alpha(Math.abs(pad.axes[0]) + Math.abs(pad.axes[1]))}` + : 'transparent' + } + style={{ + transform: `translateX(${pad.axes[0] * 10}px) translateY(${pad.axes[1] * 10}px)` + }} + stroke={ + pad.buttons[10]?.pressed ? theme.palette.primary.main : stroke + } + strokeWidth={strokeWidth} + d="M190.892,326.654c-15.024,0-27.252,12.228-27.252,27.258c0,15.025,12.228,27.253,27.252,27.253 c15.031,0,27.258-12.228,27.258-27.253C218.15,338.882,205.923,326.654,190.892,326.654z" + /> + 0.05 || Math.abs(pad.axes[3]) > 0.05 + ? `${Color(theme.palette.primary.main).alpha(Math.abs(pad.axes[2]) + Math.abs(pad.axes[3]))}` + : 'transparent' + } + style={{ + transform: `translateX(${pad.axes[2] * 10}px) translateY(${pad.axes[3] * 10}px)` + }} + stroke={ + pad.buttons[11]?.pressed ? theme.palette.primary.main : stroke + } + strokeWidth={strokeWidth} + d="M378.482,326.654c-15.031,0-27.258,12.228-27.258,27.258c0,15.025,12.227,27.253,27.258,27.253 c15.025,0,27.252-12.228,27.252-27.253C405.734,338.882,393.508,326.654,378.482,326.654z" + /> + + + + + + + + + diff --git a/src/components/Gamepad/GamepadSvgPs5.tsx b/src/components/Gamepad/GamepadSvgPs5.tsx index af94fb35..77c4e5c9 100644 --- a/src/components/Gamepad/GamepadSvgPs5.tsx +++ b/src/components/Gamepad/GamepadSvgPs5.tsx @@ -1,4 +1,3 @@ -/* eslint-disable prettier/prettier */ import { useTheme } from '@mui/material' import Color from 'color' @@ -13,155 +12,515 @@ const GamepadSvgPs5 = ({ stroke = 'rgba(255,255,255,0.5)', // stroke = '#0dbedc', strokeWidth = '2', - strokeWidth2 = '2.75', + strokeWidth2 = '2.75' // cover = '#1c1c1e' }: any) => { const theme = useTheme() // console.log(pad, theme, type, cover) return ( - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - 0.05 || - Math.abs(pad.axes[1]) > 0.05 - ? `${Color(theme.palette.primary.main).alpha( - Math.abs(pad.axes[0]) + Math.abs(pad.axes[1]) - )}` - : 'transparent' - } style={{ transform: `translateX(${pad.axes[0] * 10}px) translateY(${pad.axes[1] * 10}px)` }} stroke={pad.buttons[10].pressed ? theme.palette.primary.main : stroke} strokeWidth={strokeWidth2} strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" d="M306.416,375.584c0.023-6.889-3.541-13.476-9.321-17.224c-10.569-6.882-24.75-2.76-29.985,8.714c-2.449,5.329-2.449,11.69,0,17.02 c5.235,11.475,19.415,15.596,29.985,8.714C302.875,389.06,306.44,382.473,306.416,375.584z"/> + 0.05 || Math.abs(pad.axes[1]) > 0.05 + ? `${Color(theme.palette.primary.main).alpha(Math.abs(pad.axes[0]) + Math.abs(pad.axes[1]))}` + : 'transparent' + } + style={{ + transform: `translateX(${pad.axes[0] * 10}px) translateY(${pad.axes[1] * 10}px)` + }} + stroke={ + pad.buttons[10].pressed ? theme.palette.primary.main : stroke + } + strokeWidth={strokeWidth2} + strokeLinecap="round" + strokeLinejoin="round" + strokeMiterlimit="10" + d="M306.416,375.584c0.023-6.889-3.541-13.476-9.321-17.224c-10.569-6.882-24.75-2.76-29.985,8.714c-2.449,5.329-2.449,11.69,0,17.02 c5.235,11.475,19.415,15.596,29.985,8.714C302.875,389.06,306.44,382.473,306.416,375.584z" + /> - + - + - + - + - + - + - + - + - + - + - 0.05 || - Math.abs(pad.axes[3]) > 0.05 - ? `${Color(theme.palette.primary.main).alpha( - Math.abs(pad.axes[2]) + Math.abs(pad.axes[3]) - )}` - : 'transparent' - } style={{ transform: `translateX(${pad.axes[2] * 10}px) translateY(${pad.axes[3] * 10}px)` }} - stroke={pad.buttons[11].pressed ? theme.palette.primary.main : stroke} strokeWidth={strokeWidth2} strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" d="M443.582,375.584c-0.023-6.889,3.541-13.476,9.321-17.224c10.569-6.882,24.75-2.76,29.985,8.714c2.449,5.329,2.449,11.69,0,17.02 c-5.235,11.475-19.415,15.596-29.985,8.714C447.124,389.06,443.559,382.473,443.582,375.584z"/> + 0.05 || Math.abs(pad.axes[3]) > 0.05 + ? `${Color(theme.palette.primary.main).alpha(Math.abs(pad.axes[2]) + Math.abs(pad.axes[3]))}` + : 'transparent' + } + style={{ + transform: `translateX(${pad.axes[2] * 10}px) translateY(${pad.axes[3] * 10}px)` + }} + stroke={ + pad.buttons[11].pressed ? theme.palette.primary.main : stroke + } + strokeWidth={strokeWidth2} + strokeLinecap="round" + strokeLinejoin="round" + strokeMiterlimit="10" + d="M443.582,375.584c-0.023-6.889,3.541-13.476,9.321-17.224c10.569-6.882,24.75-2.76,29.985,8.714c2.449,5.329,2.449,11.69,0,17.02 c-5.235,11.475-19.415,15.596-29.985,8.714C447.124,389.06,443.559,382.473,443.582,375.584z" + /> - + - + - + - + - + - + - + - + } + strokeWidth={strokeWidth2} + strokeLinecap="round" + strokeLinejoin="round" + strokeMiterlimit="10" + d="M547.917,322.309c-6.169-0.022-12.077,3.175-15.433,8.352c-6.166,9.466-2.471,22.18,7.808,26.866 c4.772,2.194,10.478,2.194,15.249,0c10.279-4.687,13.974-17.4,7.808-26.866C559.994,325.485,554.086,322.287,547.917,322.309z" + /> - + - + - + - + - + diff --git a/src/components/GlobalActionBar.tsx b/src/components/GlobalActionBar.tsx index 825b39f9..344e2fd3 100644 --- a/src/components/GlobalActionBar.tsx +++ b/src/components/GlobalActionBar.tsx @@ -1,38 +1,36 @@ -/* eslint-disable prettier/prettier */ -import { Button, IconButton, Slider, Stack, useTheme } from '@mui/material'; -import type { SxProps, Theme } from '@mui/material'; -import { Brightness7, PauseOutlined, PlayArrow } from '@mui/icons-material'; +import { Button, IconButton, Slider, Stack, useTheme } from '@mui/material' +import type { SxProps, Theme } from '@mui/material' +import { Brightness7, PauseOutlined, PlayArrow } from '@mui/icons-material' import { useEffect, useState } from 'react' -import useStore from '../store/useStore'; +import useStore from '../store/useStore' const GlobalActionBar = ({ - className, - sx, - height, - type, + className = undefined, + sx = undefined, + height = 15, + type = 'icon' }: { - className?: string | undefined; - sx?: SxProps | undefined; - height?: number; - type?: 'button' | 'icon' | 'indicator'; + className?: string | undefined + sx?: SxProps | undefined + height?: number + type?: 'button' | 'icon' | 'indicator' }) => { - const theme = useTheme(); + const theme = useTheme() - const getSystemConfig = useStore((state) => state.getSystemConfig); - const setSystemConfig = useStore((state) => state.setSystemConfig); - const globalBrightness = useStore((state) => state.config.global_brightness); + const getSystemConfig = useStore((state) => state.getSystemConfig) + const setSystemConfig = useStore((state) => state.setSystemConfig) + const globalBrightness = useStore((state) => state.config.global_brightness) const setSystemSetting = (setting: string, value: any) => { - setSystemConfig({ [setting]: value }).then(() => getSystemConfig()); - }; + setSystemConfig({ [setting]: value }).then(() => getSystemConfig()) + } - const [brightness, setBrightness] = useState(globalBrightness * 100); - const paused = useStore((state) => state.paused); - const togglePause = useStore((state) => state.togglePause); + const [brightness, setBrightness] = useState(globalBrightness * 100) + const paused = useStore((state) => state.paused) + const togglePause = useStore((state) => state.togglePause) useEffect(() => { - setBrightness(globalBrightness * 100); - }, [globalBrightness]); - + setBrightness(globalBrightness * 100) + }, [globalBrightness]) return ( { - togglePause(); + togglePause() }} style={{ - margin: '0 8px 0 8px', color: '#fff' + margin: '0 8px 0 8px', + color: '#fff' }} > - {paused ? : } + {paused ? ( + + ) : ( + + )} ) : type === 'button' ? ( ) : ( // @@ -86,19 +94,20 @@ const GlobalActionBar = ({ backgroundRepeat: 'no-repeat', backgroundPosition: 'center', backgroundImage: + // eslint-disable-next-line 'url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' class=\'MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiBox-root css-kaxv2e\' focusable=\'false\' aria-hidden=\'true\' viewBox=\'0 0 24 24\' data-testid=\'WbSunnySharpIcon\'%3E%3Cpath fill=\'%23666\' d=\'m6.76 4.84-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7 1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91 1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z\'%3E%3C/path%3E%3C/svg%3E")', // 'url(/icon.png)', // opacity: 0, '& input:after': { // content: 'hi', - }, + } }, '& .MuiSlider-track': { - height: 3, + height: 3 }, '& .MuiSlider-rail': { backgroundColor: '#666', - height: 3, + height: 3 }, '& .MuiSliderValueLabel ': { fontSize: 12, @@ -107,13 +116,13 @@ const GlobalActionBar = ({ backgroundColor: 'unset', color: theme.palette.text.primary, '&:before': { - display: 'none', + display: 'none' }, '& *': { background: 'transparent', - color: theme.palette.mode === 'dark' ? '#fff' : '#000', - }, - }, + color: theme.palette.mode === 'dark' ? '#fff' : '#000' + } + } }} // valueLabelDisplay="on" value={brightness} @@ -122,18 +131,12 @@ const GlobalActionBar = ({ min={0} max={100} onChangeCommitted={(_e, val) => - typeof val === 'number' && setSystemSetting('global_brightness', val / 100) + typeof val === 'number' && + setSystemSetting('global_brightness', val / 100) } /> - ); -}; - -GlobalActionBar.defaultProps = { - className: undefined, - sx: undefined, - height: 15, - type: 'icon', -}; + ) +} -export default GlobalActionBar; +export default GlobalActionBar diff --git a/src/components/Icons/BladeIcon/BladeIcon.interface.tsx b/src/components/Icons/BladeIcon/BladeIcon.interface.tsx index 4112e2bc..4f4b857f 100644 --- a/src/components/Icons/BladeIcon/BladeIcon.interface.tsx +++ b/src/components/Icons/BladeIcon/BladeIcon.interface.tsx @@ -28,12 +28,3 @@ export interface BladeIconProps { */ style?: Record } - -export const BladeIconDefaultProps = { - colorIndicator: false, - name: 'MusicNote', - className: '', - style: {}, - scene: false, - card: false -} diff --git a/src/components/Icons/BladeIcon/BladeIcon.stories.tsx b/src/components/Icons/BladeIcon/BladeIcon.stories.tsx index 9e1b50b3..2e9395be 100644 --- a/src/components/Icons/BladeIcon/BladeIcon.stories.tsx +++ b/src/components/Icons/BladeIcon/BladeIcon.stories.tsx @@ -1,4 +1,4 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react' +import { StoryFn, Meta } from '@storybook/react' // eslint-disable-next-line import BladeIcon from './BladeIcon'; @@ -14,10 +14,10 @@ export default { showPanel: true } } -} as ComponentMeta +} as Meta // eslint-disable-next-line -const Template: ComponentStory = (args) => ; +const Template: StoryFn = (args) => ; export const Default = Template.bind({}) Default.args = {} diff --git a/src/components/Icons/BladeIcon/BladeIcon.tsx b/src/components/Icons/BladeIcon/BladeIcon.tsx index c98a4d3f..ee4b20f2 100644 --- a/src/components/Icons/BladeIcon/BladeIcon.tsx +++ b/src/components/Icons/BladeIcon/BladeIcon.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/no-danger */ import { Icon } from '@mui/material' import Wled from '../Wled' import RazerMouse from '../RzrMouse' @@ -21,7 +20,7 @@ import { import { camelToSnake } from '../../../utils/helpers' import '../../../assets/materialdesignicons.css' import '../../../index.css' -import { BladeIconDefaultProps, BladeIconProps } from './BladeIcon.interface' +import { BladeIconProps } from './BladeIcon.interface' import HomeAssistantLogo from '../HomeAssistant' import NovationLogo from '../Novation' @@ -98,7 +97,7 @@ function BladeIcon({ ) : name.startsWith('yz:logo2') ? ( ( ) -FX.defaultProps = { - stroke: '#ffffff', - currentColor: 'transparent' -} - export default FX diff --git a/src/components/Icons/waves.tsx b/src/components/Icons/waves.tsx index 7703a41f..0969a3a5 100644 --- a/src/components/Icons/waves.tsx +++ b/src/components/Icons/waves.tsx @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ import { ios } from '../../utils/helpers' const WaveLines = ({ diff --git a/src/components/Integrations/QLC/DialogAddEventListener.tsx b/src/components/Integrations/QLC/DialogAddEventListener.tsx index a895e64a..0139bae8 100644 --- a/src/components/Integrations/QLC/DialogAddEventListener.tsx +++ b/src/components/Integrations/QLC/DialogAddEventListener.tsx @@ -1,4 +1,3 @@ -/* eslint-disable prettier/prettier */ import React from 'react' import { styled } from '@mui/material/styles' import PropTypes from 'prop-types' @@ -23,29 +22,24 @@ const PREFIX = 'DialogAddEventListener' const classes = { root: `${PREFIX}-root`, - paper: `${PREFIX}-paper`, + paper: `${PREFIX}-paper` } const Root = styled('div')(({ theme }) => ({ [`& .${classes.root}`]: { width: '100%', maxWidth: 360, - backgroundColor: theme.palette.background.paper, + backgroundColor: theme.palette.background.paper }, [`& .${classes.paper}`]: { width: '80%', - maxHeight: 535, - }, + maxHeight: 535 + } })) function ConfirmationDialogRaw(props: any) { - const { - onClose, - value: valueProp, - open, - ...other - } = props + const { onClose, value: valueProp, open, ...other } = props const [valueState, setValue] = React.useState(valueProp) const [checkButtonType, setButtonType] = React.useState(false) const [checkSliderType, setSliderType] = React.useState(false) @@ -56,7 +50,7 @@ function ConfirmationDialogRaw(props: any) { const [formData, setformData] = React.useState({ event_type: null, event_filter: {}, - qlc_payload: null, + qlc_payload: null }) const [qlcData, setqlcData] = React.useState([]) const radioGroupRef = React.useRef(null) @@ -78,10 +72,8 @@ function ConfirmationDialogRaw(props: any) { const temp = (qlcInfo && qlcInfo?.qlc_widgets) || [] const QLCWidgets = - temp.length > 0 - ? [...temp].sort( - (a: string[], b: string[]) => parseInt(a[0], 10) - parseInt(b[0], 10) - ) + temp.length > 0 // eslint-disable-next-line + ? [...temp].sort((a: string[], b: string[]) => parseInt(a[0], 10) - parseInt(b[0], 10)) : [] React.useEffect(() => { @@ -91,7 +83,9 @@ function ConfirmationDialogRaw(props: any) { }, [valueProp, open]) const handleEntering = () => { - if (radioGroupRef.current != null) { /* empty */ } + if (radioGroupRef.current != null) { + /* empty */ + } } const handleCancel = () => { @@ -101,7 +95,7 @@ function ConfirmationDialogRaw(props: any) { const handleOk = () => { onClose(valueState) const data = JSON.stringify(formData) - // eslint-disable-next-line no-console + console.error('QLCFormEventTest1', data) createQlcListener(formData).then(() => { getIntegrations() @@ -118,8 +112,8 @@ function ConfirmationDialogRaw(props: any) { const newSwitchState = { ...formData, qlc_payload: { - ...newqlcPayload, - }, + ...newqlcPayload + } } setSwitchValue(event.target.checked) setqlcData(qlcDatanewArr) @@ -127,7 +121,7 @@ function ConfirmationDialogRaw(props: any) { } else if (event.target.name === 'qlc_payload') { const qlcDatanewArr: any = qlcData.slice() const qlcDataObj: any = { - [event.target.value[0]]: 0, + [event.target.value[0]]: 0 } qlcDatanewArr[0] = qlcDataObj setSwitchValue(false) @@ -137,21 +131,25 @@ function ConfirmationDialogRaw(props: any) { const newSwitchState = { ...formData, qlc_payload: { - ...newqlcPayload, - }, + ...newqlcPayload + } } setformData(newSwitchState) - } else if (event.target.name === 'scene_id' || event.target.name === 'effect_name') { - value = JSON.parse(value); - const filterKey = value?.event_type === 'scene_activated' ? 'scene_id' : 'effect_name'; + } else if ( + event.target.name === 'scene_id' || + event.target.name === 'effect_name' + ) { + value = JSON.parse(value) + const filterKey = + value?.event_type === 'scene_activated' ? 'scene_id' : 'effect_name' const newFormState = { ...formData, event_filter: { - [filterKey]: value?.event_name, + [filterKey]: value?.event_name }, - event_type: value?.event_type, - }; - setformData(newFormState); + event_type: value?.event_type + } + setformData(newFormState) } else { const qlcDatanewArr: any = qlcData.slice() qlcDatanewArr[0][event.target.value[0]] = value @@ -159,8 +157,8 @@ function ConfirmationDialogRaw(props: any) { const newSliderState = { ...formData, qlc_payload: { - ...newqlcPayload, - }, + ...newqlcPayload + } } setSliderValue(value) setformData(newSliderState) @@ -216,8 +214,8 @@ function ConfirmationDialogRaw(props: any) { const newSwitchState = { ...formData, qlc_payload: { - ...newqlcPayload, - }, + ...newqlcPayload + } } setqlcData(qlcDatanewArr) @@ -227,7 +225,7 @@ function ConfirmationDialogRaw(props: any) { newArr[index].value = target const qlcDatanewArr: any = qlcData.slice() const qlcDataObj = { - [event.target.value[0]]: 0, + [event.target.value[0]]: 0 } if (qlcDatanewArr[index + 1] === undefined) { qlcDatanewArr.push(qlcDataObj) @@ -243,8 +241,8 @@ function ConfirmationDialogRaw(props: any) { const newSwitchState = { ...formData, qlc_payload: { - ...newqlcPayload, - }, + ...newqlcPayload + } } setformData(newSwitchState) } else { @@ -254,8 +252,8 @@ function ConfirmationDialogRaw(props: any) { const newSliderState = { ...formData, qlc_payload: { - ...newqlcPayload, - }, + ...newqlcPayload + } } setqlcData(qlcDatanewArr) @@ -271,7 +269,7 @@ function ConfirmationDialogRaw(props: any) { value: '', switchValue: false, showSwitch: false, - showSlider: false, + showSlider: false } const newArr: any = dropDownRenderList.slice() @@ -289,8 +287,8 @@ function ConfirmationDialogRaw(props: any) { const newSwitchState = { ...formData, qlc_payload: { - ...newqlcPayload, - }, + ...newqlcPayload + } } setformData(newSwitchState) return setdropDownRenderList(newArr) @@ -298,7 +296,7 @@ function ConfirmationDialogRaw(props: any) { const marks = [ { value: 1, label: '1' }, - { value: 255, label: '255' }, + { value: 255, label: '255' } ] delete other.deviceList @@ -320,15 +318,15 @@ function ConfirmationDialogRaw(props: any) { Trigger Actions based on Events. - - - Event - + + Event @@ -374,8 +373,8 @@ function ConfirmationDialogRaw(props: any) { setWledIp(e.target.value)} + > + + Select WLED Device + + {wledIps.map((wledip) => ( + + {wledip} + + ))} + + )} + + setIp(e.target.value)} + /> + + Camera + + + + + + + + + {/* + Capture: + {imageSrc && webcam} */} + {/* Test Images + {imageSrcs.map((img, key) => ( + webcam + ))} */} +
+ {!isCalibrating && !isAdjusting && ( + <> + + Cut Top + setIgnoreTop(v as number)} + /> + + + Cut Left + setIgnoreLeft(v as number)} + /> + + + Cut Bottom + setIgnoreBottom(v as number)} + /> + + + Cut Right + setIgnoreRight(v as number)} + /> + + + )} + {!isCalibrating && isAdjusting && ( + <> + + Threshold + setThreshold(v as number)} + /> + + + Led + { + setSingleLed(v as number) + oneLed(v as number) + }} + /> + + + )} + + {!isAdjusting && ( + <> +
+ Base Image: +
+ baseImage +
+
+
+
+
+ + )} + {isAdjusting && ( + <> + Diff Image: + diff + + )} + + ) +} + +export default Webcam diff --git a/src/components/Webcam/matrixUtils.ts b/src/components/Webcam/matrixUtils.ts new file mode 100644 index 00000000..7246562b --- /dev/null +++ b/src/components/Webcam/matrixUtils.ts @@ -0,0 +1,13 @@ +export function transpose( + inputX: number, + inputY: number, + inputWidth: number, + inputHeight: number, + outputWidth: number, + outputHeight: number +): { x: number; y: number } { + const outputX = Math.floor((inputX / inputWidth) * outputWidth) + const outputY = Math.floor((inputY / inputHeight) * outputHeight) + + return { x: outputX, y: outputY } +} diff --git a/src/components/Webcam/pixelMapper.js b/src/components/Webcam/pixelMapper.js new file mode 100644 index 00000000..16650961 --- /dev/null +++ b/src/components/Webcam/pixelMapper.js @@ -0,0 +1,65 @@ +import { + wled, + wait, + processImages, + iterativeOnOnlyOne, + iterativeOn, + getLedCount +} from './pixelUtils' + +export async function initialize(capture) { + await wled({ seg: { on: false } }) + await wait(1000) + let img = await capture() + return img +} + +export async function preadjust() { + await wled({ on: true }) + await wled({ seg: { on: true, col: [[255, 0, 0],[255, 0, 0],[255, 0, 0]]}}) +} +export async function adjust( + capture, + initialImage, + threshold = 128, + ignoreTop = 0, + ignoreLeft = 0, + ignoreBottom = 0, + ignoreRight = 0, + pointsRef +) { + let img = await capture() + processImages(initialImage, img, threshold, ignoreTop, ignoreLeft, ignoreBottom, ignoreRight, pointsRef) +} + +export async function calibrate(wledIp, waitTime, pointsRef, inputWidth = 400, inputHeight = 300, outputWidth = 50, outputHeight = 50, setPoints,addPoint, device) { + const flush = async () => { + await wled({ seg: { on: true, col: [0, 0, 0] } }) + } + const flushAll = async (pixels = 69) => { + await wled({ seg: { i: [0, pixels, '000000'] } }) + } + const leds = await getLedCount(wledIp) + await flushAll(leds) + setPoints([]) + await iterativeOnOnlyOne(waitTime / 7, pointsRef, inputWidth, inputHeight, outputWidth, outputHeight, addPoint, device) + await flush() + setPoints([]) +} + +export async function calibrateOld(wledIp, waitTime) { + const flush = async () => { + await wled({ seg: { on: true, col: [0, 0, 0] } }) + } + const flushAll = async (pixels = 69) => { + await wled({ seg: { i: [0, pixels, '000000'] } }) + } + + await iterativeOn(waitTime / 25) + await wait(waitTime) + const leds = await getLedCount(wledIp) + await flushAll(leds) + + await iterativeOnOnlyOne(waitTime / 7) + await flush() +} diff --git a/src/components/Webcam/pixelUtils.js b/src/components/Webcam/pixelUtils.js new file mode 100644 index 00000000..771c57e7 --- /dev/null +++ b/src/components/Webcam/pixelUtils.js @@ -0,0 +1,226 @@ +import useStore from '../../store/useStore' +import { transpose } from './matrixUtils' + +export const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) + +export const wled = async (body) => { + const wledIp = useStore.getState().videoMapper.wledIp + try { + const response = await fetch(`http://${wledIp}/json`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + }) + const json = await response.json() + return json + } catch (error) { + console.error(error) + } +} +export const wledGet = async (path = 'state') => { + const wledIp = useStore.getState().videoMapper.wledIp + if (!wledIp || wledIp === '') { + return + } + try { + const response = await fetch(`http://${wledIp}/json/${path}`) + const json = await response.json() + return json || response + } catch (error) { + console.error(error) + } +} + +export const getLedCount = async (wledIp) => { + try { + const response = await fetch(`http://${wledIp}/json/info`) + const info = await response.json() + return info.leds.count + } catch (error) { + console.error(error) + } + +} + +export const oneLed = async (led) => { + const state = await wledGet('state') + const segments = state.seg + let totalLedCount = 0 + + // First, turn all LEDs off + for (const segment of segments) { + const allLedsOff = { + seg: { + id: segment.id, + i: [0, segment.len, '000000'] + } + } + await wled(allLedsOff) + } + + // Then, turn on the specified LED + for (const segment of segments) { + totalLedCount += segment.len + if (totalLedCount >= led) { + const ledInSegment = led - (totalLedCount - segment.len) + const singleLedOn = { + seg: { + id: segment.id, + i: [ledInSegment, ledInSegment + 1, 'FFFFFF'] + } + } + await wled(singleLedOn) + break + } + } +} + + +export function decodeBase64ToImageData(base64String) { + const base64Data = base64String.split(',')[1] + const imageData = atob(base64Data) + const buffer = new Uint8Array(imageData.length) + for (let i = 0; i < imageData.length; i++) { + buffer[i] = imageData.charCodeAt(i) + } + return buffer +} + +export async function createImageFromData(imageData) { + const blob = new Blob([imageData], { type: 'image/webp' }) + const url = URL.createObjectURL(blob) + const img = new Image() + img.src = url + await new Promise((resolve) => { + img.onload = resolve + }) + return img +} + +export function calculateImageDifference(img1, img2, threshold = 128, ignoreTop = 0, ignoreLeft = 0, ignoreBottom = 0, ignoreRight = 0) { + // console.log("BROOOOO", img1.width, img1.height, ignoreTop, ignoreLeft, ignoreBottom, ignoreRight) + const width = img1.width - ignoreLeft - ignoreRight + const height = img1.height - ignoreTop - ignoreBottom + const canvas = document.createElement('canvas', {willReadFrequently: true}) + canvas.width = width + canvas.height = height + const context = canvas.getContext('2d', {willReadFrequently: true}) + // context.drawImage(img1, 0, 0, width, height) + context.drawImage(img1, ignoreLeft, ignoreTop, width, height, 0, 0, width, height) + const imageData1 = context.getImageData(0, 0, width, height).data + context.clearRect(0, 0, width, height) + // context.drawImage(img2, 0, 0, width, height) + context.drawImage(img2, ignoreLeft, ignoreTop, width, height, 0, 0, width, height) + const imageData2 = context.getImageData(0, 0, width, height).data + const diffData = new Uint8ClampedArray(width * height * 4) + + let whitePixels = [] + + for (let i = 0; i < width * height * 4; i += 4) { + const diffRed = Math.abs(imageData1[i] - imageData2[i]) + const diffGreen = Math.abs(imageData1[i + 1] - imageData2[i + 1]) + const diffBlue = Math.abs(imageData1[i + 2] - imageData2[i + 2]) + const diffAlpha = 255 + const avgDiff = (diffRed + diffGreen + diffBlue) / 3 // Combine differences (simple average) Ignore the alpha channel + const outputValue = avgDiff > threshold ? 255 : 0 + diffData[i] = outputValue + diffData[i + 1] = outputValue + diffData[i + 2] = outputValue + diffData[i + 3] = diffAlpha // Keep alpha channel intact + + if (outputValue === 255) { + const x = (i / 4) % width + const y = Math.floor((i / 4) / width) + whitePixels.push({ x, y }) + } + } + + const whitePixelsCount = whitePixels.length + const whitePixelsCenter = whitePixels.reduce((acc, { x, y }) => ({ x: acc.x + x, y: acc.y + y }), { x: 0, y: 0 }) + whitePixelsCenter.x /= whitePixelsCount + whitePixelsCenter.y /= whitePixelsCount + + context.putImageData(new ImageData(diffData, width, height), 0, 0) + + // Draw a small red circle at the center of the white area + context.beginPath() + context.arc(whitePixelsCenter.x, whitePixelsCenter.y, 5, 0, 2 * Math.PI, false) + context.fillStyle = 'red' + context.fill() + + return { diffImage: canvas.toDataURL('image/webp'), whitePixelsCenter } +} + +export async function processImages(img1b64, img2b64, threshold = 128, ignoreTop = 0, ignoreLeft = 0, ignoreBottom = 0, ignoreRight = 0, pointsRef) { + try { + const imageData1 = decodeBase64ToImageData(img1b64) + const image1 = await createImageFromData(imageData1) + const imageData2 = decodeBase64ToImageData(img2b64) + const image2 = await createImageFromData(imageData2) + const diffData = calculateImageDifference(image1, image2, threshold, ignoreTop, ignoreLeft, ignoreBottom, ignoreRight) + const diffImageBase64 = diffData.diffImage + // console.log("BROOOOO", diffData.whitePixelsCenter) + if (pointsRef) pointsRef.current = diffData.whitePixelsCenter + const diffImageElement = document.getElementById('diffImage') + diffImageElement.src = diffImageBase64 + } catch (error) { + console.error('Error processing images:', error) + } +} + +export const iterativeOnOnlyOne = async (waitingTime, pointsRef, inputWidth = 400, inputHeight = 300, outputWidth = 50, outputHeight = 50, addPoint, device) => { + const state = await wledGet() + const segments = state.seg + let totalPreviousLength = 0; // Initialize total length of previous segments + for (const segment of segments) { + for (let i = 0; i < segment.len + 1; i++) { + const singleLedOn = { + seg: { + id: segment.id, + i: + i === 0 + ? [i, i + 1, 'FFFFFF', i + 1, segment.len, '000000'] + : [ + 0, + i, + '000000', + i, + i + 1, + 'FFFFFF', + i + 1, + segment.len, + '000000' + ] + } + } + await wled(singleLedOn) + const transposed = transpose(pointsRef?.current?.x, pointsRef?.current?.y, inputWidth, inputHeight, outputWidth, outputHeight) + if (!isNaN(transposed.x) && !isNaN(transposed.y)) { + // console.log("transposed", {...transposed, segment: segment.id, led: i, device}) + addPoint({...transposed, segment: segment.id, led: totalPreviousLength + i, device}) // Add totalPreviousLength to i + await wait(waitingTime) + } + } + totalPreviousLength += segment.len; // Update total length of previous segments + } +} + + +export const iterativeOn = async (waitingTime) => { + const state = await wledGet() + const segments = state.seg + for (const segment of segments) { + for (let i = 0; i < segment.len; i++) { + const singleLedOn = { + seg: { + id: segment.id, + i: [i, i + 1, 'FFFFFF', i + 1, segment.len, '000000'] + } + } + await wled(singleLedOn) + await wait(waitingTime) + } + } +} diff --git a/src/pages/Device/Cloud/Cloud.tsx b/src/pages/Device/Cloud/Cloud.tsx index d5807f9f..219aaacc 100644 --- a/src/pages/Device/Cloud/Cloud.tsx +++ b/src/pages/Device/Cloud/Cloud.tsx @@ -1,7 +1,3 @@ -/* eslint-disable no-alert */ -/* eslint-disable consistent-return */ -/* eslint-disable no-restricted-syntax */ -/* eslint-disable @typescript-eslint/no-empty-function */ import { useEffect, useState } from 'react' import { useTheme } from '@mui/material/styles' import { diff --git a/src/pages/Device/Cloud/CloudComponents.tsx b/src/pages/Device/Cloud/CloudComponents.tsx index 1d17d1ea..ff42a939 100644 --- a/src/pages/Device/Cloud/CloudComponents.tsx +++ b/src/pages/Device/Cloud/CloudComponents.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/require-default-props */ import React from 'react' import axios from 'axios' @@ -12,7 +11,6 @@ export const cloud = axios.create({ }) cloud.interceptors.request.use((config) => { - // eslint-disable-next-line no-param-reassign config.headers.Authorization = `Bearer ${localStorage.getItem('jwt')}` return config }) diff --git a/src/pages/Device/Device.tsx b/src/pages/Device/Device.tsx index dcd8336f..6e1e7ca0 100644 --- a/src/pages/Device/Device.tsx +++ b/src/pages/Device/Device.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-restricted-syntax */ import { useEffect } from 'react' import { Grid, Typography } from '@mui/material' import { Link, useParams } from 'react-router-dom' diff --git a/src/pages/Device/Effects.tsx b/src/pages/Device/Effects.tsx index 5b825b30..0f5250a7 100644 --- a/src/pages/Device/Effects.tsx +++ b/src/pages/Device/Effects.tsx @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/indent */ -/* eslint-disable consistent-return */ -/* eslint-disable no-restricted-syntax */ + import { useEffect, useState } from 'react' import { Button, diff --git a/src/pages/Device/Frequencies.tsx b/src/pages/Device/Frequencies.tsx index 0205f7a3..f6903970 100644 --- a/src/pages/Device/Frequencies.tsx +++ b/src/pages/Device/Frequencies.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-no-duplicate-props */ import { useState } from 'react' import Card from '@mui/material/Card' import CardContent from '@mui/material/CardContent' diff --git a/src/pages/Device/Presets.tsx b/src/pages/Device/Presets.tsx index 02c7b345..5bcdff7a 100644 --- a/src/pages/Device/Presets.tsx +++ b/src/pages/Device/Presets.tsx @@ -47,14 +47,12 @@ const PresetsCard = ({ virtual, effectType, presets, style }: any) => { `configs?user.username=${localStorage.getItem('username')}` ) if (response.status !== 200) { - // eslint-disable-next-line no-alert alert('No Access') return } const res = await response.data setCloudConfigs(res) } catch (error) { - // eslint-disable-next-line no-console console.log(error) } } @@ -102,7 +100,6 @@ const PresetsCard = ({ virtual, effectType, presets, style }: any) => { const getCloudPresets = async () => { const response = await cloud.get(`presets?effect.ledfx_id=${effectType}`) if (response.status !== 200) { - // eslint-disable-next-line no-alert alert('No Access') return } @@ -267,9 +264,7 @@ const PresetsCard = ({ virtual, effectType, presets, style }: any) => { popoverStyle={{ padding: '0.5rem' }} color="primary" variant="outlined" - onSingleClick={() => { - // eslint-disable-next-line no-console - }} + onSingleClick={() => {}} content={ e.key === 'Enter' && handleAddPreset()} diff --git a/src/pages/Device/StreamTo.tsx b/src/pages/Device/StreamTo.tsx index 695ad220..361292b0 100644 --- a/src/pages/Device/StreamTo.tsx +++ b/src/pages/Device/StreamTo.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/require-default-props */ import Card from '@mui/material/Card' import CardContent from '@mui/material/CardContent' import CardHeader from '@mui/material/CardHeader' diff --git a/src/pages/Devices/DeviceCard/DeviceCard.interface.tsx b/src/pages/Devices/DeviceCard/DeviceCard.interface.tsx index 1851c46c..1165be35 100644 --- a/src/pages/Devices/DeviceCard/DeviceCard.interface.tsx +++ b/src/pages/Devices/DeviceCard/DeviceCard.interface.tsx @@ -31,6 +31,10 @@ export interface DeviceCardProps { * Do not send to leds */ previewOnly?: boolean + /** + * Dummy Device + */ + dummy?: boolean /** * TransitionTime of the Device */ @@ -99,27 +103,3 @@ export interface DeviceCardProps { * JSX styles */ } -export const DeviceCardDefaults: DeviceCardProps = { - deviceName: 'My Wled', - online: true, - effectName: undefined, - virtId: 'yz-quad', - index: 1, - handleDeleteDevice: () => console.log('DELETING DEVICE'), // eslint-disable-line no-console - handleEditVirtual: () => console.log('EDITING VIRTUAL'), // eslint-disable-line no-console - handleEditDevice: () => console.log('EDITING DEVICE'), // eslint-disable-line no-console - handleClearEffect: () => console.log('CLEARING EFFECT'), // eslint-disable-line no-console - handlePlayPause: () => console.log('PLAY/PAUSE'), // eslint-disable-line no-console - linkTo: '/', - additionalStyle: {}, - iconName: 'wled', - colorIndicator: true, - isPlaying: true, - isStreaming: false, - previewOnly: true, - isEffectSet: true, - transitionTime: 5, - isDevice: 'yz-quad', - graphsActive: true, - showMatrix: false -} diff --git a/src/pages/Devices/DeviceCard/DeviceCard.stories.tsx b/src/pages/Devices/DeviceCard/DeviceCard.stories.tsx index 8277dd09..7bcbfbec 100644 --- a/src/pages/Devices/DeviceCard/DeviceCard.stories.tsx +++ b/src/pages/Devices/DeviceCard/DeviceCard.stories.tsx @@ -1,4 +1,4 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react' +import { StoryFn, Meta } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' // eslint-disable-next-line @@ -17,10 +17,10 @@ export default { showPanel: true } } -} as ComponentMeta +} as Meta // eslint-disable-next-line -const Template: ComponentStory = (args) => ( +const Template: StoryFn = (args) => ( ) diff --git a/src/pages/Devices/DeviceCard/DeviceCard.tsx b/src/pages/Devices/DeviceCard/DeviceCard.tsx index 5d317a93..ad7b1901 100644 --- a/src/pages/Devices/DeviceCard/DeviceCard.tsx +++ b/src/pages/Devices/DeviceCard/DeviceCard.tsx @@ -15,6 +15,7 @@ import { Clear, Delete, Pause, + PestControl, PlayArrow, SyncProblem } from '@mui/icons-material' @@ -24,37 +25,38 @@ import EditVirtuals from '../EditVirtuals/EditVirtuals' import PixelGraph from '../../../components/PixelGraph' import BladeIcon from '../../../components/Icons/BladeIcon/BladeIcon' import useStyle from './DeviceCard.styles' -import { DeviceCardDefaults, DeviceCardProps } from './DeviceCard.interface' +import { DeviceCardProps } from './DeviceCard.interface' /** * Pixelgraphs will not connect via Websocket in Storybook * */ const DeviceCard = ({ - deviceName, - online, - virtId, - index, - handleDeleteDevice, - handleEditVirtual, - handleEditDevice, - handleClearEffect, - handlePlayPause, - linkTo, - additionalStyle, - iconName, - graphsActive, - colorIndicator, - effectName, - isPlaying, - isStreaming, - previewOnly, - isEffectSet, - transitionTime, - isDevice, graphsMulti, - showMatrix, - activateDevice + activateDevice, + deviceName = 'My Wled', + online = true, + effectName = undefined, + virtId = 'yz-quad', + index = 1, + handleDeleteDevice = () => console.log('DELETING DEVICE'), + handleEditVirtual = () => console.log('EDITING VIRTUAL'), + handleEditDevice = () => console.log('EDITING DEVICE'), + handleClearEffect = () => console.log('CLEARING EFFECT'), + handlePlayPause = () => console.log('PLAY/PAUSE'), + linkTo = '/', + additionalStyle = {}, + iconName = 'wled', + colorIndicator = true, + isPlaying = true, + isStreaming = false, + previewOnly = true, + dummy = false, + isEffectSet = true, + transitionTime = 5, + isDevice = 'yz-quad', + graphsActive = true, + showMatrix = false }: DeviceCardProps) => { const classes = useStyle() const theme = useTheme() @@ -226,6 +228,11 @@ const DeviceCard = ({ )} + {dummy && ( + + )}
{!(window.localStorage.getItem('guestmode') === 'activated') ? ( @@ -295,7 +302,7 @@ const DeviceCard = ({ unmountOnExit className={classes.buttonBarMobile} > - {/* eslint-disable-next-line */} + {}
d === virtual) || ''] ?.active_virtuals!.length > 0) ) - + // console.log(fade, isActive) const handleDeleteDevice = () => { deleteVirtual(virtuals[virtual]?.id).then(() => { getVirtuals() @@ -92,11 +91,15 @@ const DeviceCardWrapper = ({ devices[Object.keys(devices).find((d) => d === virtual) || ''] ?.active_virtuals!.length > 0 ) - }, [virtuals, devices]) + }, [virtuals, devices, virtual]) const order = () => { if (showActiveDevicesFirst) { - if (virtuals[virtual]?.config.rows > 1 && !virtuals[virtual]?.effect.name && showMatrix) { + if ( + virtuals[virtual]?.config.rows > 1 && + !virtuals[virtual]?.effect.name && + showMatrix + ) { return -1 } if (virtuals[virtual]?.config.rows > 1 && showMatrix) { @@ -157,6 +160,10 @@ const DeviceCardWrapper = ({ virtuals[virtual]?.config && virtuals[virtual]?.config.preview_only } + dummy={ + devices[Object.keys(devices).find((d) => d === virtual) || '']?.type === + 'dummy' + } isEffectSet={Object.keys(virtuals[virtual]?.effect)?.length > 0} additionalStyle={{ order: order() diff --git a/src/pages/Devices/Devices.tsx b/src/pages/Devices/Devices.tsx index e94f470f..c945e717 100644 --- a/src/pages/Devices/Devices.tsx +++ b/src/pages/Devices/Devices.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-plusplus */ import { useEffect } from 'react' import { makeStyles } from '@mui/styles' import { Alert, Collapse } from '@mui/material' diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Actions/assignPixels.ts b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/assignPixels.ts new file mode 100644 index 00000000..4e0b7f55 --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/assignPixels.ts @@ -0,0 +1,160 @@ +import { transpose } from '../../../../../utils/helpers' +import { clone, IMCell } from '../M.utils' + +const assignPixels = ({ + m, + rowN, + colN, + currentCell, + currentDevice, + selectedPixel, + direction, + setM, + closeClear, + pixelGroups, + setPixelGroups +}: { + m: IMCell[][] + rowN: number + colN: number + currentCell: number[] + currentDevice: string + selectedPixel: number | number[] + direction: string + setM: any + closeClear: any + pixelGroups: number + setPixelGroups: any +}) => { + let updatedM: IMCell[][] = clone(m) + const [col, row] = currentCell + if (typeof selectedPixel === 'number') { + updatedM[row][col] = { + deviceId: currentDevice, + pixel: selectedPixel, + group: `${row}-${col}` + } + } else { + for ( + let index = 0; + index < Math.abs(selectedPixel[1] - selectedPixel[0]); + index += 1 + ) { + const newM = { + deviceId: currentDevice, + pixel: Math.min(selectedPixel[0], selectedPixel[1]) + index, + group: `${row}-${col}` + } + if (direction.includes('right')) { + if (direction.includes('flip')) { + updatedM[row - Math.floor((index + col) / colN)][ + (index + col) % colN + ] = newM + } else { + updatedM[row + Math.floor((index + col) / colN)][ + (index + col) % colN + ] = newM + } + } else if (direction.includes('bottom')) { + if (direction.includes('flip')) { + updatedM[(index + row) % rowN][ + col - Math.floor((index + row) / rowN) + ] = newM + } else { + updatedM[(index + row) % rowN][ + col + Math.floor((index + row) / rowN) + ] = newM + } + } else if (direction.includes('left')) { + if (direction.includes('flip')) { + updatedM[row + Math.abs(Math.floor((col - index) / colN))][ + (colN + ((col - index) % colN)) % colN + ] = newM + } else { + updatedM[row - Math.abs(Math.floor((col - index) / colN))][ + (colN + ((col - index) % colN)) % colN + ] = newM + } + } else if (direction.includes('top')) { + if (direction.includes('flip')) { + updatedM[(rowN + ((row - index) % rowN)) % rowN][ + col + Math.abs(Math.floor((row - index) / rowN)) + ] = newM + } else { + updatedM[(rowN + ((row - index) % rowN)) % rowN][ + col - Math.abs(Math.floor((row - index) / rowN)) + ] = newM + } + } + } + } + if (direction.includes('right-snake')) { + if (direction.includes('flip')) { + for (let i = row; i >= 0; i -= 1) { + const currentRow = [...updatedM[i]] + if ((i + row) % 2 === 1) updatedM[i] = currentRow.reverse() + } + } else { + for (let i = row; i < rowN; i += 1) { + const currentRow = [...updatedM[i]] + if ((i + row) % 2 === 1) updatedM[i] = currentRow.reverse() + } + } + } + if (direction.includes('bottom-snake')) { + if (direction.includes('flip')) { + const mat = clone(updatedM) + const temp = transpose(mat) + for (let i = col; i >= 0; i -= 1) { + const currentCol = [...temp[i]] + if ((i + col) % 2 === 1) temp[i] = currentCol.reverse() + } + updatedM = transpose(temp) + } else { + const mat = clone(updatedM) + const temp = transpose(mat) + for (let i = col; i < colN; i += 1) { + const currentCol = [...temp[i]] + if ((i + col) % 2 === 1) temp[i] = currentCol.reverse() + } + updatedM = transpose(temp) + } + } + if (direction.includes('left-snake')) { + if (direction.includes('flip')) { + for (let i = row; i < rowN; i += 1) { + const currentRow = [...updatedM[i]] + if ((i + row) % 2 === 1) updatedM[i] = currentRow.reverse() + } + } else { + for (let i = row; i >= 0; i -= 1) { + const currentRow = [...updatedM[i]] + if ((i + row) % 2 === 1) updatedM[i] = currentRow.reverse() + } + } + } + if (direction.includes('top-snake')) { + if (direction.includes('flip')) { + const mat = clone(updatedM) + const temp = transpose(mat) + for (let i = col; i < colN; i += 1) { + const currentCol = [...temp[i]] + if ((i + col) % 2 === 1) temp[i] = currentCol.reverse() + } + updatedM = transpose(temp) + } else { + const mat = clone(updatedM) + const temp = transpose(mat) + for (let i = col; i >= 0; i -= 1) { + const currentCol = [...temp[i]] + if ((i + col) % 2 === 1) temp[i] = currentCol.reverse() + } + updatedM = transpose(temp) + } + } + setPixelGroups(pixelGroups + 1) + setM(updatedM) + closeClear() +} + +export default assignPixels diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Actions/hexColor.ts b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/hexColor.ts new file mode 100644 index 00000000..77a7a3b8 --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/hexColor.ts @@ -0,0 +1,16 @@ +function hexColor(encodedString: string, mode?: string) { + if (mode === 'uncompressed' || !encodedString) { + return [] + } + const decodedString = atob(encodedString) + const charCodes = Array.from(decodedString).map((char) => char.charCodeAt(0)) + const colors = Array.from({ length: charCodes.length / 3 }, (_, i) => { + const r = charCodes[i * 3] + const g = charCodes[i * 3 + 1] + const b = charCodes[i * 3 + 2] + return { r, g, b } + }) + return colors +} + +export default hexColor diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Actions/interfaces.ts b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/interfaces.ts new file mode 100644 index 00000000..cdd4578e --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/interfaces.ts @@ -0,0 +1,16 @@ +export interface IMoveGroup { + m: any + rowN: number + colN: number + selectedGroup: string + setError: any + setM: any +} + +export interface IMoveGroupHorizontal { + m: any + rowN: number + colN: number + selectedGroup: string + setM: any +} diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupDown.ts b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupDown.ts new file mode 100644 index 00000000..77bb0c4a --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupDown.ts @@ -0,0 +1,39 @@ +import { clone } from '../M.utils' +import { IMoveGroup } from './interfaces' + +const moveSelectedGroupDown = ({ + m, + rowN, + colN, + selectedGroup, + setError, + setM +}: IMoveGroup) => { + const updatedM = clone(m) + const conflictingCells = [] // Array to store conflicting cells + + for (let i = rowN - 1; i >= 0; i--) { + for (let j = 0; j < colN; j += 1) { + if (m[i][j].group === selectedGroup) { + const targetRow = i + 1 + if (targetRow < rowN) { + if (updatedM[targetRow][j].deviceId !== '') { + // Conflict detected, add cell to conflictingCells + conflictingCells.push({ row: targetRow, col: j }) + } else { + updatedM[targetRow][j] = m[i][j] + updatedM[i][j] = { deviceId: '', pixel: 0, group: '' } + } + } + } + } + } + + if (conflictingCells.length > 0) { + setError(conflictingCells) // Set error with conflicting cells + } else { + setM(updatedM) // Update matrix if no conflicts + } +} + +export default moveSelectedGroupDown diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupLeft.ts b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupLeft.ts new file mode 100644 index 00000000..e24713d5 --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupLeft.ts @@ -0,0 +1,26 @@ +import { clone } from '../M.utils' +import { IMoveGroupHorizontal } from './interfaces' + +const moveSelectedGroupLeft = ({ + m, + rowN, + colN, + selectedGroup, + setM +}: IMoveGroupHorizontal) => { + const updatedM = clone(m) + for (let i = 0; i < rowN; i++) { + for (let j = 0; j < colN; j++) { + if (m[i][j].group === selectedGroup) { + const targetCol = j - 1 + if (targetCol >= 0 && m[i][0].deviceId === '') { + updatedM[i][targetCol] = m[i][j] + updatedM[i][j] = { deviceId: '', pixel: 0, group: '' } + } + } + } + } + setM(updatedM) +} + +export default moveSelectedGroupLeft diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupRight.ts b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupRight.ts new file mode 100644 index 00000000..61b12075 --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupRight.ts @@ -0,0 +1,26 @@ +import { clone } from '../M.utils' +import { IMoveGroupHorizontal } from './interfaces' + +const moveSelectedGroupRight = ({ + m, + rowN, + colN, + selectedGroup, + setM +}: IMoveGroupHorizontal) => { + const updatedM = clone(m) + for (let i = 0; i < rowN; i++) { + for (let j = colN - 1; j >= 0; j--) { + if (m[i][j].group === selectedGroup) { + const targetCol = j + 1 + if (targetCol < colN && m[i][colN - 1].deviceId === '') { + updatedM[i][targetCol] = m[i][j] + updatedM[i][j] = { deviceId: '', pixel: 0, group: '' } + } + } + } + } + setM(updatedM) +} + +export default moveSelectedGroupRight diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupUp.ts b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupUp.ts new file mode 100644 index 00000000..73a0b246 --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Actions/moveSelectedGroupUp.ts @@ -0,0 +1,40 @@ +import { clone } from '../M.utils' +import { IMoveGroup } from './interfaces' + +const moveSelectedGroupUp = ({ + m, + rowN, + colN, + selectedGroup, + setError, + setM +}: IMoveGroup) => { + const updatedM = clone(m) + const conflictingCells = [] + + for (let i = 0; i < rowN; i++) { + for (let j = 0; j < colN; j += 1) { + if (m[i][j].group === selectedGroup) { + const targetRow = i - 1 // Calculate target row above + if (targetRow >= 0) { + // Check if within matrix bounds + if (updatedM[targetRow][j].deviceId !== '') { + // Conflict detected, add cell to conflictingCells + conflictingCells.push({ row: targetRow, col: j }) + } else { + updatedM[targetRow][j] = m[i][j] + updatedM[i][j] = { deviceId: '', pixel: 0, group: '' } + } + } + } + } + } + + if (conflictingCells.length > 0) { + setError(conflictingCells) // Set error with conflicting cells + } else { + setM(updatedM) // Update matrix if no conflicts + } +} + +export default moveSelectedGroupUp diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/AssignPixelDialog.tsx b/src/pages/Devices/EditVirtuals/EditMatrix/AssignPixelDialog.tsx new file mode 100644 index 00000000..33f180f8 --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/AssignPixelDialog.tsx @@ -0,0 +1,130 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + MenuItem, + Select +} from '@mui/material' +import MDialogTitle from './MDialogTitle' +import BladeFrame from '../../../../components/SchemaForm/components/BladeFrame' +import MSwitch from './MSwitch' +import MFillSelector from './MFillSelector' +import MSlider from './MSlider' +import assignPixels from './Actions/assignPixels' +import { IDir } from './M.utils' +import useStore from '../../../../store/useStore' + +const AssignPixelDialog = ({ + open, + closeClear, + currentCell, + m, + currentDevice, + setCurrentDevice, + deviceRef, + group, + setGroup, + direction, + handleDirectionChange, + selectedPixel, + handleSliderChange, + rowN, + colN, + setM, + pixelGroups, + setPixelGroups, + clearPixel +}: { + open: boolean + closeClear: any + currentCell: [number, number] + m: any + currentDevice: string + setCurrentDevice: any + deviceRef: any + group: boolean + setGroup: any + direction: IDir + handleDirectionChange: any + selectedPixel: number | number[] + handleSliderChange: any + rowN: number + colN: number + setM: any + pixelGroups: any + setPixelGroups: any + clearPixel: any +}) => { + const devices = useStore((state) => state.devices) + + return ( + closeClear()} + open={open} + PaperProps={{ sx: { width: '100%', maxWidth: 320 } }} + > + + + + + + {currentDevice && ( + <> + + {group && ( + + )} + + + )} + + + + + + + ) +} + +export default AssignPixelDialog diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Draggable.tsx b/src/pages/Devices/EditVirtuals/EditMatrix/Draggable.tsx new file mode 100644 index 00000000..41740cd3 --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Draggable.tsx @@ -0,0 +1,51 @@ +/* eslint-disable @typescript-eslint/indent */ +import { useDraggable } from '@dnd-kit/core' + +function Draggable({ children, id, ...props }: any) { + const { attributes, listeners, setNodeRef, transform } = useDraggable({ + id + }) + const style = transform + ? { + width: '100px', + transform: `translate3d(${transform.x}px, ${transform.y}px, 0)` + } + : undefined + + // useDndMonitor({ + // onDragStart(event) { + // const x = (event.active.id as string).split('-')[0] + // const y = (event.active.id as string).split('-')[1] + // const cell = m[x][y] + // const cellsOfGroup = m.flat().filter((c: any) => c.group === cell.group) + + // console.log('onDragStart', cellsOfGroup, event) + // }, + // onDragMove(event) { + // console.log('onDragMove', event) + // }, + // onDragOver(event) { + // console.log('onDragOver', event) + // }, + // onDragEnd(event) { + // console.log('onDragEnd', event) + // }, + // onDragCancel(event) { + // console.log('onDragCancel', event) + // } + // }) + return ( +
+ {children} +
+ ) +} + +export default Draggable diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Droppable.tsx b/src/pages/Devices/EditVirtuals/EditMatrix/Droppable.tsx new file mode 100644 index 00000000..09df49e2 --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Droppable.tsx @@ -0,0 +1,45 @@ +import { useDroppable } from '@dnd-kit/core' +import useStyles from './M.styles' +import { IMCell } from './M.utils' + +function Droppable({ + id, + children, + cell, + bg, + opacity = 1, + onContextMenu +}: { + id: string + children: React.ReactNode + cell?: IMCell + bg: string + opacity?: number + onContextMenu?: React.MouseEventHandler | undefined +}) { + const { isOver, setNodeRef } = useDroppable({ + id + }) + const style = { + opacity, + background: isOver + ? cell?.deviceId === '' + ? 'rgba(0, 255, 0, 0.1)' + : 'rgba(255, 0, 0, 0.1)' + : bg + } + const classes = useStyles() + + return ( +
+ {children} +
+ ) +} + +export default Droppable diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/M.tsx b/src/pages/Devices/EditVirtuals/EditMatrix/M.tsx index 71f92038..b114fb6a 100644 --- a/src/pages/Devices/EditVirtuals/EditMatrix/M.tsx +++ b/src/pages/Devices/EditVirtuals/EditMatrix/M.tsx @@ -1,68 +1,94 @@ -/* eslint-disable prettier/prettier */ +/* eslint-disable @typescript-eslint/indent */ import { useEffect, useRef, useState, FC } from 'react' -import { Box, Button, Dialog, DialogActions, DialogContent, MenuItem, Select, Typography } from '@mui/material' +import { Box, Button, Stack } from '@mui/material' import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch' -import { transpose } from '../../../../utils/helpers' +import { DndContext, DragEndEvent } from '@dnd-kit/core' +import { restrictToFirstScrollableAncestor } from '@dnd-kit/modifiers' +import { + MCell, + clone, + getBackgroundColor, + getMaxRange, + getOpacity +} from './M.utils' +import { reverseProcessArray } from './processMatrix' +import type { IMCell, IDir } from './M.utils' import useStore from '../../../../store/useStore' import useStyles from './M.styles' -import { MCell, clone, getMaxRange } from './M.utils' -import type { IMCell, IDir } from './M.utils' -import BladeFrame from '../../../../components/SchemaForm/components/BladeFrame' import MWrapper from './MWrapper' import MControls from './MControls' -import MFillSelector from './MFillSelector' -import MDialogTitle from './MDialogTitle' -import MSwitch from './MSwitch' -import MSlider from './MSlider' +import Droppable from './Droppable' +import Draggable from './Draggable' +import Pixel from './Pixel' +import hexColor from './Actions/hexColor' +import MContextMenu from './MContextMenu' +import AssignPixelDialog from './AssignPixelDialog' +import { Ledfx } from '../../../../api/ledfx' const EditMatrix: FC<{ virtual: any }> = ({ virtual }) => { const classes = useStyles() const deviceRef = useRef(null) + + const [showPixelGraph, setShowPixelGraph] = useState(false) + const pixelGraphs = useStore((state) => state.pixelGraphs) const devices = useStore((state) => state.devices) + const virtuals = useStore((state) => state.virtuals) + const mode = useStore((state) => state.config).transmission_mode + const addDevice = useStore((state) => state.addDevice) + const getDevices = useStore((state) => state.getDevices) + const getVirtuals = useStore((state) => state.getVirtuals) + const updateVirtual = useStore((state) => state.updateVirtual) + const setEffect = useStore((state) => state.setEffect) + const points = useStore((state) => state.points) + + const [error, setError] = useState<{ row: number; col: number }[]>([]) const [currentDevice, setCurrentDevice] = useState('') - const [rowN, setRowNumber] = useState(virtual.config.rows || 4) - const [colN, setColNumber] = useState(Math.ceil(virtual.pixel_count/(virtual.config.rows || 1)) || 6) + const [rowN, setRowNumber] = useState(virtual.config.rows || 8) + const [colN, setColNumber] = useState( + Math.ceil(virtual.pixel_count / (virtual.config.rows || 1)) || 8 + ) const [currentCell, setCurrentCell] = useState<[number, number]>([-1, -1]) const [open, setOpen] = useState(false) const [group, setGroup] = useState(false) const [selectedPixel, setSelectedPixel] = useState(0) const [direction, setDirection] = useState('right') - const [m, setM] = useState(Array(rowN).fill(Array(colN).fill(MCell))) + const [m, setM] = useState( + Array(rowN).fill(Array(colN).fill(MCell)) + ) + const [pixelGroups, setPixelGroups] = useState(0) + const [selectedGroup, setSelectedGroup] = useState('0-0') + const [pixels, setPixels] = useState([]) + const [move, setMove] = useState(false) + const [dnd, setDnd] = useState(false) + const [anchorEl, setAnchorEl] = useState(null) + const [isDragging, setIsDragging] = useState(false) + const [hoveringCell, setHoveringCell] = useState<[number, number]>([-1, -1]) - const [pixels, setPixels] = useState([]); - const pixelGraphs = useStore((state) => state.pixelGraphs); - const virtuals = useStore((state) => state.virtuals); - const mode = useStore((state) => state.config).transmission_mode + const decodedPixels = + mode === 'compressed' + ? pixels && pixels.length && hexColor(pixels, mode) + : pixels - function hexColor(encodedString: string) { - if (mode === 'uncompressed' || !encodedString) { - return [] + const handleContextMenu = ( + e: React.MouseEvent, + currentColIndex: number, + currentRowIndex: number, + yzcolumn: IMCell + ) => { + e.preventDefault() + e.stopPropagation() + setCurrentCell([currentColIndex, currentRowIndex]) + setCurrentDevice(yzcolumn.deviceId !== '' ? yzcolumn.deviceId : '') + setSelectedPixel(yzcolumn.pixel || 0) + if (currentRowIndex > -1 && currentColIndex > -1) { + if (m[currentRowIndex][currentColIndex]?.deviceId !== '') { + setAnchorEl(e.currentTarget) + } else { + setOpen(true) + } } - const decodedString = atob(encodedString) - const charCodes = Array.from(decodedString).map(char => char.charCodeAt(0)) - const colors = Array.from({length: charCodes.length / 3}, (_, i) => { - const r = charCodes[i * 3] - const g = charCodes[i * 3 + 1] - const b = charCodes[i * 3 + 2] - return {r, g, b} - }) - return colors } - const decodedPixels = mode === 'compressed' ? pixels && pixels.length && hexColor(pixels) : pixels - - useEffect(() => { - const handleWebsockets = (e: any) => { - if (e.detail.id === virtual.id) { - setPixels(e.detail.pixels); - } - }; - document.addEventListener('visualisation_update', handleWebsockets); - return () => { - document.removeEventListener('visualisation_update', handleWebsockets); - }; - }, [virtuals, pixelGraphs]); - const closeClear = () => { setOpen(false) setCurrentDevice('') @@ -82,110 +108,11 @@ const EditMatrix: FC<{ virtual: any }> = ({ virtual }) => { } } - const assignPixels = () => { - let updatedM: IMCell[][] = clone(m) - const [col, row] = currentCell - if (typeof selectedPixel === 'number') { - updatedM[row][col] = { deviceId: currentDevice, pixel: selectedPixel } - } else { - for ( let index = 0; index < Math.abs(selectedPixel[1] - selectedPixel[0]); index += 1 ) { - const newM = { deviceId: currentDevice, pixel: Math.min(selectedPixel[0], selectedPixel[1]) + index } - if (direction.includes('right')) { - if (direction.includes('flip')) { - updatedM[row - Math.floor((index + col) / colN)][(index + col) % colN] = newM - } else { - updatedM[row + Math.floor((index + col) / colN)][(index + col) % colN] = newM - } - } else if (direction.includes('bottom')) { - if (direction.includes('flip')) { - updatedM[(index + row) % rowN][col - Math.floor((index + row) / rowN)] = newM - } else { - updatedM[(index + row) % rowN][col + Math.floor((index + row) / rowN)] = newM - } - } else if (direction.includes('left')) { - if (direction.includes('flip')) { - updatedM[row + Math.abs(Math.floor((col - index) / colN))][(colN + ((col - index) % colN)) % colN] = newM - } else { - updatedM[row - Math.abs(Math.floor((col - index) / colN))][(colN + ((col - index) % colN)) % colN] = newM - } - } else if (direction.includes('top')) { - if (direction.includes('flip')) { - updatedM[(rowN + ((row - index) % rowN)) % rowN][col + Math.abs(Math.floor((row - index) / rowN))] = newM - } else { - updatedM[(rowN + ((row - index) % rowN)) % rowN][col - Math.abs(Math.floor((row - index) / rowN))] = newM - } - } - } - } - if (direction.includes('right-snake')) { - if (direction.includes('flip')) { - for (let i = row; i >= 0; i -= 1) { - const currentRow = [...updatedM[i]] - if ((i + row) % 2 === 1) updatedM[i] = currentRow.reverse() - } - } else { - for (let i = row; i < rowN; i += 1) { - const currentRow = [...updatedM[i]] - if ((i + row) % 2 === 1) updatedM[i] = currentRow.reverse() - } - } - } - if (direction.includes('bottom-snake')) { - if (direction.includes('flip')) { - const mat = clone(updatedM) - const temp = transpose(mat) - for (let i = col; i >= 0; i -= 1) { - const currentCol = [...temp[i]] - if ((i + col) % 2 === 1) temp[i] = currentCol.reverse() - } - updatedM = transpose(temp) - } else { - const mat = clone(updatedM) - const temp = transpose(mat) - for (let i = col; i < colN; i += 1) { - const currentCol = [...temp[i]] - if ((i + col) % 2 === 1) temp[i] = currentCol.reverse() - } - updatedM = transpose(temp) - } - } - if (direction.includes('left-snake')) { - if (direction.includes('flip')) { - for (let i = row; i < rowN; i += 1) { - const currentRow = [...updatedM[i]] - if ((i + row) % 2 === 1) updatedM[i] = currentRow.reverse() - } - } else { - for (let i = row; i >= 0; i -= 1) { - const currentRow = [...updatedM[i]] - if ((i + row) % 2 === 1) updatedM[i] = currentRow.reverse() - } - } - } - if (direction.includes('top-snake')) { - if (direction.includes('flip')) { - const mat = clone(updatedM) - const temp = transpose(mat) - for (let i = col; i < colN; i += 1) { - const currentCol = [...temp[i]] - if ((i + col) % 2 === 1) temp[i] = currentCol.reverse() - } - updatedM = transpose(temp) - } else { - const mat = clone(updatedM) - const temp = transpose(mat) - for (let i = col; i >= 0; i -= 1) { - const currentCol = [...temp[i]] - if ((i + col) % 2 === 1) temp[i] = currentCol.reverse() - } - updatedM = transpose(temp) - } - } - setM(updatedM) - closeClear() - } - - const handleSliderChange = ( e: Event, newPixelRange: number | [number, number], activeThumb: number ) => { + const handleSliderChange = ( + e: Event, + newPixelRange: number | [number, number], + activeThumb: number + ) => { if (typeof newPixelRange !== 'number') { const [col, row] = currentCell const maxRange = getMaxRange(direction, row, col, rowN, colN) @@ -199,7 +126,10 @@ const EditMatrix: FC<{ virtual: any }> = ({ virtual }) => { adjustedLeftThumb = adjustedRightThumb - maxRange } } - const updatedSelectedPixel = direction === 'top' ? [adjustedRightThumb, adjustedLeftThumb] : [adjustedLeftThumb, adjustedRightThumb] + const updatedSelectedPixel = + direction === 'top' + ? [adjustedRightThumb, adjustedLeftThumb] + : [adjustedLeftThumb, adjustedRightThumb] setSelectedPixel(updatedSelectedPixel) } else { setSelectedPixel(newPixelRange) @@ -209,11 +139,50 @@ const EditMatrix: FC<{ virtual: any }> = ({ virtual }) => { const clearPixel = () => { const updatedM = clone(m) const [col, row] = currentCell - updatedM[row][col] = { deviceId: '', pixel: 0 } + updatedM[row][col] = { deviceId: '', pixel: 0, group: 0 } setM(updatedM) closeClear() } + const handleDragEnd = (event: DragEndEvent) => { + // console.log(parent, event) + if (event.over && event.over.id) { + const updatedM = clone(m) + const [xOver, yOver] = (event.over.id as string).split('-').map(Number) + if (updatedM[yOver][xOver].deviceId === '') { + const [xActive, yActive] = (event.active.id as string) + .split('-') + .map(Number) + updatedM[yOver][xOver] = updatedM[yActive][xActive] + updatedM[yActive][xActive] = { deviceId: '', pixel: 0, group: 0 } + setM(updatedM) + } + } + setIsDragging(false) + } + + /** + * Update the pixel-graphs when the virtual changes + */ + useEffect(() => { + const handleWebsockets = (e: any) => { + if (e.detail.id === virtual.id) { + setPixels(e.detail.pixels) + } + } + if (showPixelGraph && virtual.id) { + document.addEventListener('visualisation_update', handleWebsockets) + } else { + document.removeEventListener('visualisation_update', handleWebsockets) + } + return () => { + document.removeEventListener('visualisation_update', handleWebsockets) + } + }, [virtuals, pixelGraphs, showPixelGraph, virtual]) + + /** + * Set the selected pixel when the group changes + */ useEffect(() => { if (group) { if (typeof selectedPixel === 'number') { @@ -222,70 +191,309 @@ const EditMatrix: FC<{ virtual: any }> = ({ virtual }) => { } else if (typeof selectedPixel !== 'number') { setSelectedPixel(selectedPixel[0]) } - }, [group]) + }, [group, selectedPixel]) + /** + * Set the matrix when the row or column numbers change + */ useEffect(() => { setM(Array(rowN).fill(Array(colN).fill(MCell))) }, [rowN, colN]) - return - - - -
-
- {m.map((yzrow, currentRowIndex) =>
- {yzrow.map((yzcolumn: IMCell, currentColIndex: number) => ( - { + if (virtual.segments.length === 0) { + if (!Object.values(devices).some((d) => d.id === `gap-${virtual.id}`)) { + // add new device + addDevice({ + type: 'dummy', + config: { + center_offset: 0, + icon_name: 'mdi:eye-off', + name: `gap-${virtual.id}`, + pixel_count: 4096, + refresh_rate: 64 + } + }).then(() => { + Ledfx(`/api/virtuals/${virtual.id}`, 'POST', { + segments: [ + [ + `gap-${virtual.id}`, + 0, + virtual.config.rows * virtual.config.rows - 1, + false + ] + ] + }).then(() => { + getDevices() + getVirtuals().then(() => { + setEffect( + virtual.id, + 'equalizer2d', + { bands: virtual.config.rows }, + true + ).then(() => { + updateVirtual(virtual.id, true) + }) + }) + }) + }) + } + } + }, []) + + /** + * Update the row and column numbers when the virtual changes + */ + useEffect(() => { + setRowNumber(virtual.config.rows || 8) + setColNumber( + Math.ceil(virtual.pixel_count / (virtual.config.rows || 1)) || 8 + ) + }, [virtual]) - opacity: yzcolumn.deviceId !== '' ? 1 : 0.3, - }} onContextMenu={(e) => { - e.preventDefault() - setCurrentCell([currentColIndex, currentRowIndex]) - setCurrentDevice(yzcolumn.deviceId !== '' ? yzcolumn.deviceId : '') - setSelectedPixel(yzcolumn.pixel || 0) - setOpen(true) - }}> - {yzcolumn.deviceId !== '' && ( -
- {devices[yzcolumn.deviceId].config.name} - {yzcolumn.pixel} -
- )} -
- ))} + /** + * Update the matrix when a new point is added from the video mapper + */ + useEffect(() => { + if (points.length > 0) { + const updatedM = clone(m) + const p = points[points.length - 1] + if ( + !isNaN(p.x) && + !isNaN(p.y) && + p.x > 0 && + p.y > 0 && + p.x <= colN && + p.y <= rowN + ) { + updatedM[p.y - 1][p.x - 1] = { + deviceId: p.device, + pixel: p.led, + group: p.segment + 1 + } + setM(updatedM) + } + } + }, [points]) + + return ( + + + + + + + + +
+
+ handleDragEnd(e)} + onDragOver={(e) => + setHoveringCell( + (e.over?.id as any)?.split('-').map(Number) || [-1, -1] + ) + } + onDragStart={() => { + // console.log('zy', e.active?.id, hoveringCell, selectedGroup) + setIsDragging(true) + }} + > + {m.map((yzrow, currentRowIndex) => ( +
+ {yzrow.map((yzcolumn: IMCell, currentColIndex: number) => { + const bg = getBackgroundColor( + mode, + decodedPixels, + pixels, + currentRowIndex, + colN, + currentColIndex + ) + const op = getOpacity(move, yzcolumn, selectedGroup) + return ( + -1 && hoveringCell[1] > -1 + ? m[hoveringCell[1]][hoveringCell[0]] + : undefined + } + id={`${currentColIndex}-${currentRowIndex}`} + key={`col-${currentColIndex}`} + bg={bg} + opacity={op} + onContextMenu={(e) => + handleContextMenu( + e, + currentColIndex, + currentRowIndex, + yzcolumn + ) + } + > + + handleContextMenu( + e, + currentColIndex, + currentRowIndex, + yzcolumn + ) + } + sx={{ + backgroundColor: bg, + opacity: op + }} + > + {dnd ? ( + m[currentRowIndex][currentColIndex].deviceId !== + '' ? ( + + + ) => setAnchorEl(e.currentTarget)} + isDragging={isDragging} + /> + + ) : null + ) : ( + + ) => setAnchorEl(e.currentTarget)} + isDragging={isDragging} + /> + )} + + + ) + })} +
+ ))} +
- )} + setAnchorEl(null)} + currentCell={currentCell} + m={m} + setOpen={setOpen} + setMove={setMove} + /> +
- closeClear()} open={open} PaperProps={{ sx: { width: '100%', maxWidth: 320 } }}> - - - - - - {currentDevice && <> - - {group && } - - } - - - - - - -
- - - } + + + + ) +} export default EditMatrix diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/M.utils.tsx b/src/pages/Devices/EditVirtuals/EditMatrix/M.utils.tsx index 9d332320..31f8b912 100644 --- a/src/pages/Devices/EditVirtuals/EditMatrix/M.utils.tsx +++ b/src/pages/Devices/EditVirtuals/EditMatrix/M.utils.tsx @@ -19,9 +19,10 @@ export type IDir = export type IMCell = { deviceId: string pixel: number + group?: string } -export const MCell: IMCell = { deviceId: '', pixel: 0 } +export const MCell: IMCell = { deviceId: '', pixel: 0, group: '0-0' } /** * Calculates the maximum available pixel given the input data @@ -62,3 +63,41 @@ export const getMaxRange = ( * deep clone */ export const clone = (input: any) => JSON.parse(JSON.stringify(input)) + +export const getOpacity = ( + move: boolean, + yzcolumn: IMCell, + selectedGroup: string +) => { + if (move && yzcolumn?.group === selectedGroup) { + return 1 + } else if ( + (move && yzcolumn?.group !== selectedGroup) || + selectedGroup === '' + ) { + return 0.3 + } else if (yzcolumn.deviceId !== '') { + return 1 + } else { + return 0.3 + } +} + +export const getBackgroundColor = ( + mode: 'compressed' | 'uncompressed' | undefined, + decodedPixels: any[] | undefined, + pixels: any[][], + currentRowIndex: number, + colN: number, + currentColIndex: number +) => { + if (mode === 'compressed' && decodedPixels) { + return decodedPixels[currentRowIndex * colN + currentColIndex] + ? `rgb(${Object.values(decodedPixels[currentRowIndex * colN + currentColIndex])})` + : '#222' + } else if (pixels && pixels[0] && pixels[0].length) { + return `rgb(${pixels[0][currentRowIndex * colN + currentColIndex]},${pixels[1][currentRowIndex * colN + currentColIndex]},${pixels[2][currentRowIndex * colN + currentColIndex]})` + } else { + return '#222' + } +} diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/MContextMenu.tsx b/src/pages/Devices/EditVirtuals/EditMatrix/MContextMenu.tsx new file mode 100644 index 00000000..a7c229fa --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/MContextMenu.tsx @@ -0,0 +1,67 @@ +import { Menu, MenuItem } from '@mui/material' + +const MContextMenu = ({ + anchorEl, + closeContextMenu, + setOpen, + setSelectedGroup, + setMove, + currentCell, + m, + setDnd +}: { + anchorEl: any + closeContextMenu: any + setOpen: any + setSelectedGroup: any + setMove: any + currentCell: [number, number] + m: any + setDnd: any +}) => { + const contextMenuOpen = Boolean(anchorEl) + return ( + { + e.preventDefault() + closeContextMenu() + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button' + }} + > + { + setOpen(true) + }} + > + Edit + + { + setSelectedGroup(m[currentCell[1]][currentCell[0]].group || '0-0') + setDnd(true) + closeContextMenu() + }} + > + Move Pixel in DND + + { + setSelectedGroup(m[currentCell[1]][currentCell[0]].group || '0-0') + setMove(true) + closeContextMenu() + }} + > + Move Group + + Clear + + ) +} + +export default MContextMenu diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/MControls.tsx b/src/pages/Devices/EditVirtuals/EditMatrix/MControls.tsx index a480f15f..4d97267d 100644 --- a/src/pages/Devices/EditVirtuals/EditMatrix/MControls.tsx +++ b/src/pages/Devices/EditVirtuals/EditMatrix/MControls.tsx @@ -1,14 +1,43 @@ import { + ArrowBack, + ArrowDownward, + ArrowForward, + ArrowUpward, + Cancel, + ControlCamera, + PanTool, Rotate90DegreesCw, Save, SwapHoriz, SwapVert } from '@mui/icons-material' -import { Box, Button, Slider, Stack } from '@mui/material' +import { + Alert, + Box, + Button, + // Collapse, + IconButton, + Slider, + Stack, + Typography +} from '@mui/material' +import { useEffect, useState } from 'react' +import Tab from '@mui/material/Tab' +import TabContext from '@mui/lab/TabContext' +import TabList from '@mui/lab/TabList' +import TabPanel from '@mui/lab/TabPanel' import { Ledfx } from '../../../../api/ledfx' import Popover from '../../../../components/Popover/Popover' import { transpose } from '../../../../utils/helpers' import { MCell } from './M.utils' +import { processArray } from './processMatrix' +import moveSelectedGroupUp from './Actions/moveSelectedGroupUp' +import moveSelectedGroupLeft from './Actions/moveSelectedGroupLeft' +import moveSelectedGroupRight from './Actions/moveSelectedGroupRight' +import moveSelectedGroupDown from './Actions/moveSelectedGroupDown' +import useStore from '../../../../store/useStore' +import Webcam from '../../../../components/Webcam/Webcam' +// import useStore from '../../../../store/useStore' const MControls = ({ rowN, @@ -17,99 +46,308 @@ const MControls = ({ setColNumber, virtual, m, - setM -}: any) => { + setM, + move, + dnd, + setMove, + setDnd, + selectedGroup, + setError +}: { + rowN: number + colN: number + setRowNumber: any + setColNumber: any + virtual: any + m: any + setM: any + move: boolean + dnd: boolean + setMove: any + setDnd: any + selectedGroup: string + setError: any +}) => { + // const infoAlerts = useStore((state) => state.ui.infoAlerts) + // const setInfoAlerts = useStore((state) => state.ui.setInfoAlerts) + const [tab, setTab] = useState('1') + const getVirtuals = useStore((state) => state.getVirtuals) + const getDevices = useStore((state) => state.getDevices) + const features = useStore((state) => state.features) + + const handleChange = (event: React.SyntheticEvent, newValue: string) => { + if (newValue === '1') setDnd(false) + if (newValue === '2') setDnd(true) + setTab(newValue) + } + + useEffect(() => { + if (dnd && tab !== '2') setTab('2') + if (!dnd && tab !== '1') setTab('1') + }, [dnd]) + return ( - <> - - Rows: - - - typeof newRowNumber === 'number' && setRowNumber(newRowNumber) - } - /> - - {rowN} - - - Columns: - - - typeof newColNumber === 'number' && setColNumber(newColNumber) - } - /> - - {colN} - - - - - - + /> + + {rowN} - - { - setM(Array(rowN).fill(Array(colN).fill(MCell))) - }} - /> + + + Columns: + + + + typeof newColNumber === 'number' && setColNumber(newColNumber) + } + /> + + {colN} + + + + + + + + + { + setM(Array(rowN).fill(Array(colN).fill(MCell))) + }} + /> - + + - + + } + iconPosition="start" + label="Move Group" + value + /> + + {move ? ( + + + + moveSelectedGroupUp({ + m, + rowN, + colN, + selectedGroup, + setError, + setM + }) + } + > + + + + + moveSelectedGroupLeft({ + m, + rowN, + colN, + selectedGroup, + setM + }) + } + > + + + setMove(false)}> + + + + moveSelectedGroupRight({ + m, + rowN, + colN, + selectedGroup, + setM + }) + } + > + + + + + moveSelectedGroupDown({ + m, + rowN, + colN, + selectedGroup, + setError, + setM + }) + } + > + + + + + ) : ( + + + Right-Click an element to move a group. +
+ Groups can only be moved with the UI buttons +
+
+ )} + + + + } + iconPosition="start" + label="Canvas" + value="1" + /> + } + iconPosition="start" + label="DND" + value="2" + /> + + + + + Canvas Mode +
    +
  • Use Mousewheel to Zoom
  • +
  • Use left-click with drag&drop to move around
  • +
  • Use right-click to:
  • +
      +
    • assign pixel or pixel-group
    • +
    • edit a pixel
    • +
    • clear a pixel
    • +
    • move a pixel-group
    • +
    +
  • Enter DND mode to move pixels individually
  • +
+
+
+ + + DND Mode +
    +
  • move pixels individually with your mouse
  • +
+
+
+
+ {features.matrix_cam && } +
) } - export default MControls diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/MWrapper.tsx b/src/pages/Devices/EditVirtuals/EditMatrix/MWrapper.tsx index b5297a18..f076cef1 100644 --- a/src/pages/Devices/EditVirtuals/EditMatrix/MWrapper.tsx +++ b/src/pages/Devices/EditVirtuals/EditMatrix/MWrapper.tsx @@ -1,26 +1,25 @@ -import { Alert } from '@mui/material' +import { Box } from '@mui/material' -const MWrapper = ({ children }: any) => { +const MWrapper = ({ children, move }: any) => { return ( -
- - Concept Draft -
    -
  • Use Mousewheel to Zoom
  • -
  • Use left-click with drag&drop to move around
  • -
  • Use right-click to assign Pixels
  • -
-
{children} -
+ ) } diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/Pixel.tsx b/src/pages/Devices/EditVirtuals/EditMatrix/Pixel.tsx new file mode 100644 index 00000000..ab621995 --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/Pixel.tsx @@ -0,0 +1,104 @@ +import { Box, Typography } from '@mui/material' +import { IMCell } from './M.utils' +import useStore from '../../../../store/useStore' + +const Pixel = ({ + m, + currentColIndex, + classes, + currentRowIndex, + move, + decodedPixels, + colN, + pixels, + yzcolumn, + selectedGroup, + error, + setCurrentCell, + setCurrentDevice, + setSelectedPixel, + openContextMenu, + isDragging +}: { + m: IMCell[][] + currentColIndex: number + classes: any + currentRowIndex: number + move: boolean + decodedPixels: any + colN: number + pixels: any + yzcolumn: IMCell + selectedGroup: string + error: { + row: number + col: number + }[] + setCurrentCell: any + setCurrentDevice: any + setSelectedPixel: any + openContextMenu: any + isDragging: boolean +}) => { + const devices = useStore((state) => state.devices) + const mode = useStore((state) => state.config).transmission_mode + if (error.length > 0) console.log(isDragging, error) + return ( + e.row === currentRowIndex && e.col === currentColIndex + // ) + // ? '1px solid red' + // : '1px solid #000', + backgroundColor: + mode === 'compressed' && decodedPixels + ? decodedPixels[currentRowIndex * colN + currentColIndex] + ? `rgb(${Object.values(decodedPixels[currentRowIndex * colN + currentColIndex])})` + : '#222' + : pixels && pixels[0] && pixels[0].length + ? `rgb(${pixels[0][currentRowIndex * colN + currentColIndex]},${pixels[1][currentRowIndex * colN + currentColIndex]},${pixels[2][currentRowIndex * colN + currentColIndex]})` + : '#222', + opacity: isDragging + ? 0.3 + : move && yzcolumn?.group === selectedGroup + ? 1 + : (move && yzcolumn?.group !== selectedGroup) || + selectedGroup === '' + ? 0.9 + : yzcolumn.deviceId !== '' + ? 1 + : 0.3 + }} + onContextMenu={(e) => { + e.preventDefault() + setCurrentCell([currentColIndex, currentRowIndex]) + setCurrentDevice(yzcolumn.deviceId !== '' ? yzcolumn.deviceId : '') + setSelectedPixel(yzcolumn.pixel || 0) + + if ( + currentRowIndex > -1 && + currentColIndex > -1 && + m[currentRowIndex][currentColIndex]?.deviceId !== '' + ) { + // console.log(m[currentCell[1]][currentCell[0]].group) + + openContextMenu(e) + } + // setOpen(true) + }} + > + {yzcolumn.deviceId !== '' && ( +
+ + {devices[yzcolumn.deviceId].config.name} + + {yzcolumn.pixel} +
+ )} +
+ ) +} + +export default Pixel diff --git a/src/pages/Devices/EditVirtuals/EditMatrix/processMatrix.ts b/src/pages/Devices/EditVirtuals/EditMatrix/processMatrix.ts new file mode 100644 index 00000000..13a6dbce --- /dev/null +++ b/src/pages/Devices/EditVirtuals/EditMatrix/processMatrix.ts @@ -0,0 +1,112 @@ +type InputType = { deviceId: string; pixel: number; group?: string } +type OutputType = [string, number, number, boolean] + +export function processArray( + inputArray: InputType[], + gapname = '' +): OutputType[] { + const outputArray: OutputType[] = [] + let startPixel: number | null = null + let endPixel: number | null = null + let deviceId: string = '' + let flip: boolean = false + let gapStart: number | null = null + + for (let i = 0; i < inputArray.length; i += 1) { + if (inputArray[i].deviceId !== '') { + if (deviceId === '') { + if (gapStart !== null) { + outputArray.push([`gap-${gapname}`, gapStart, i - 1, false]) + gapStart = null + } + deviceId = inputArray[i].deviceId + startPixel = inputArray[i].pixel + endPixel = inputArray[i].pixel + flip = false + } else if ( + inputArray[i].deviceId === deviceId && + Math.abs(inputArray[i].pixel - (endPixel as number)) === 1 + ) { + endPixel = inputArray[i].pixel + flip = inputArray[i].pixel < (endPixel as number) + } else { + outputArray.push([ + deviceId, + startPixel as number, + endPixel as number, + flip + ]) + deviceId = inputArray[i].deviceId + startPixel = inputArray[i].pixel + endPixel = inputArray[i].pixel + flip = false + } + } else { + if (deviceId !== '') { + outputArray.push([ + deviceId, + startPixel as number, + endPixel as number, + flip + ]) + deviceId = '' + } + if (gapStart === null) { + gapStart = i + } + } + } + + if (deviceId !== '') { + outputArray.push([deviceId, startPixel as number, endPixel as number, flip]) + } + if (gapStart !== null) { + outputArray.push([`gap-${gapname}`, gapStart, inputArray.length - 1, false]) + } + + for (let i = 0; i < outputArray.length; i++) { + const item = outputArray[i] + if (item[1] > item[2]) { + item[3] = item[1] > item[2] + ;[item[1], item[2]] = [item[2], item[1]] + } + } + + return outputArray +} + +export function reverseProcessArray( + outputArray: OutputType[], + rows?: number +): InputType[][] { + const inputArray: InputType[] = [] + const finalArray: InputType[][] = [] + let group: string = '0-0' + + for (let i = 0; i < outputArray.length; i += 1) { + const [deviceId, startPixel, endPixel, flip] = outputArray[i] + if (deviceId.startsWith('gap-')) { + for (let j = startPixel; j <= endPixel; j += 1) { + inputArray.push({ deviceId: '', pixel: j, group: '0-0' }) + } + } else if (flip) { + for (let j = endPixel; j >= startPixel; j -= 1) { + group = `${Math.floor(i / (rows || 1))}-${i % (rows || 1)}` + inputArray.push({ deviceId, pixel: j, group }) + } + } else { + for (let j = startPixel; j <= endPixel; j += 1) { + group = `${Math.floor(i / (rows || 1))}-${i % (rows || 1)}` + inputArray.push({ deviceId, pixel: j, group }) + } + } + } + + if (rows) { + for (let i = 0; i < inputArray.length; i += rows) { + finalArray.push(inputArray.slice(i, i + rows)) + } + return finalArray + } + return [inputArray] +} diff --git a/src/pages/Devices/EditVirtuals/EditVirtuals.tsx b/src/pages/Devices/EditVirtuals/EditVirtuals.tsx index ae63cadb..2738c277 100644 --- a/src/pages/Devices/EditVirtuals/EditVirtuals.tsx +++ b/src/pages/Devices/EditVirtuals/EditVirtuals.tsx @@ -1,6 +1,3 @@ -/* eslint-disable react/prop-types */ -/* eslint-disable react/require-default-props */ -/* eslint-disable @typescript-eslint/no-empty-function */ import React, { useEffect, useState } from 'react' import Button from '@mui/material/Button' import Dialog from '@mui/material/Dialog' @@ -68,7 +65,6 @@ export default function EditVirtuals({ innerKey }: any) { const [matrix, setMatrix] = useState(false) - const features = useStore((state) => state.features) const currentVirtual = useStore((state) => state.currentVirtual) const setCurrentVirtual = useStore((state) => state.setCurrentVirtual) const calibrationMode = useStore((state) => state.calibrationMode) @@ -183,7 +179,7 @@ export default function EditVirtuals({ > back - {features.matrix && ( + {virtual.config.rows > 1 && ( { const [range, setRange] = useState([s[1], s[2]]) + const handleChange = (_event: any, newValue: any) => { + if (newValue !== pixelRange) { + handleRangeSegment(newValue[0], newValue[1]) + } + } + + const throttled = useThrottledCallback(handleChange, 100) + useEffect(() => { getDevices() }, [getDevices]) @@ -24,13 +32,6 @@ const PixelSlider = ({ s, handleRangeSegment }: any) => { const pixelRange = [s[1], s[2]] - const handleChange = (_event: any, newValue: any) => { - if (newValue !== pixelRange) { - handleRangeSegment(newValue[0], newValue[1]) - } - } - const throttled = useThrottledCallback(handleChange, 100) - const marks = [ { value: 0, label: 1 }, { diff --git a/src/pages/Graph/Graph.tsx b/src/pages/Graph/Graph.tsx index ce8621a5..df2dbd20 100644 --- a/src/pages/Graph/Graph.tsx +++ b/src/pages/Graph/Graph.tsx @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/indent */ -/* eslint-disable consistent-return */ -/* eslint-disable no-restricted-syntax */ + import { useEffect, useState } from 'react' import { Box } from '@mui/material' import { useParams } from 'react-router-dom' diff --git a/src/pages/Home/Dashboard.tsx b/src/pages/Home/Dashboard.tsx index 6b77f95b..f1c2f8b0 100644 --- a/src/pages/Home/Dashboard.tsx +++ b/src/pages/Home/Dashboard.tsx @@ -1,9 +1,5 @@ /* eslint-disable @typescript-eslint/indent */ -/* eslint-disable no-console */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable no-promise-executor-return */ -/* eslint-disable no-plusplus */ -/* eslint-disable no-await-in-loop */ + import { useEffect, useState } from 'react' import { Box, diff --git a/src/pages/Home/DbConfig.tsx b/src/pages/Home/DbConfig.tsx index 0c18f7a8..95058d3b 100644 --- a/src/pages/Home/DbConfig.tsx +++ b/src/pages/Home/DbConfig.tsx @@ -1,37 +1,33 @@ -/* eslint-disable prettier/prettier */ -import { useTheme, Stack } from '@mui/material'; -import BladeFrame from '../../components/SchemaForm/components/BladeFrame'; -import DbRow from './DbRow'; -import useStore from '../../store/useStore'; +import { useTheme, Stack } from '@mui/material' +import BladeFrame from '../../components/SchemaForm/components/BladeFrame' +import DbRow from './DbRow' +import useStore from '../../store/useStore' const DbConfig = () => { - const theme = useTheme(); - const config = useStore((state) => state.config); - + const theme = useTheme() + const config = useStore((state) => state.config) return ( - - - {config.port_s && ( - - )} + + + {config.port_s && } - ); -}; + ) +} -export default DbConfig; +export default DbConfig diff --git a/src/pages/Home/DbDevices.tsx b/src/pages/Home/DbDevices.tsx index 6b6bd892..2f98dc30 100644 --- a/src/pages/Home/DbDevices.tsx +++ b/src/pages/Home/DbDevices.tsx @@ -1,4 +1,4 @@ -import { useTheme, Stack, Chip } from '@mui/material' +import { useTheme, Stack, Chip, IconButton } from '@mui/material' import { DataGrid, GridColDef, @@ -8,28 +8,154 @@ import { } from '@mui/x-data-grid' import { useNavigate } from 'react-router-dom' import { useEffect, useState } from 'react' +import { + Clear, + DeleteForever, + Edit, + Fullscreen, + Pause, + PlayArrow, + Settings, + SyncProblem +} from '@mui/icons-material' import BladeFrame from '../../components/SchemaForm/components/BladeFrame' import useStore from '../../store/useStore' import BladeIcon from '../../components/Icons/BladeIcon/BladeIcon' import PixelGraph from '../../components/PixelGraph' -// import BladeIcon from '../../components/Icons/BladeIcon/BladeIcon'; +const DeviceActions = ({ + virtId, + effect +}: { + virtId: string + effect?: boolean +}) => { + const navigate = useNavigate() + + const virtuals = useStore((state) => state.virtuals) + const updateVirtual = useStore((state) => state.updateVirtual) + const getVirtuals = useStore((state) => state.getVirtuals) + const getDevices = useStore((state) => state.getDevices) + const clearEffect = useStore((state) => state.clearEffect) + + const handlePlayPause = () => { + if (virtId && virtuals[virtId]) + updateVirtual(virtId, !virtuals[virtId].active).then(() => getVirtuals()) + } + const handleClearEffect = () => { + clearEffect(virtId).then(() => { + setTimeout(() => { + getVirtuals() + getDevices() + }, virtuals[virtId].config.transition_time * 1000) + }) + } + + return ( + <> + {effect && ( + <> + { + e.preventDefault() + e.stopPropagation() + handlePlayPause() + }} + > + {virtuals[virtId]?.active ? : } + + { + e.preventDefault() + e.stopPropagation() + handleClearEffect() + }} + > + + + {virtuals[virtId]?.config.rows > 1 && ( + { + e.preventDefault() + e.stopPropagation() + navigate(`/graph/${virtId}`) + }} + > + + + )} + + )} + 1 ? 0 : 4.2) + }} + size="small" + onClick={(e) => { + e.preventDefault() + e.stopPropagation() + handlePlayPause() + }} + > + + + { + e.preventDefault() + e.stopPropagation() + handlePlayPause() + }} + > + + + { + e.preventDefault() + e.stopPropagation() + handlePlayPause() + }} + > + + + + ) +} + +const ReconnectButton = ({ onClick }: { onClick: () => void }) => ( + { + e.preventDefault() + onClick() + }} + > + + +) const DbDevices = () => { const theme = useTheme() const navigate = useNavigate() + const devices = useStore((state) => state.devices) const virtuals = useStore((state) => state.virtuals) const graphs = useStore((state) => state.graphs) const graphsMulti = useStore((state) => state.graphsMulti) const [fade] = useState(false) const showMatrix = useStore((state) => state.showMatrix) const setPixelGraphs = useStore((state) => state.setPixelGraphs) - // const activateDevice = useStore((state) => state.activateDevice) + const activateDevice = useStore((state) => state.activateDevice) useEffect(() => { if (graphs && graphsMulti) { setPixelGraphs(Object.keys(virtuals)) } - }, [graphs, graphsMulti, setPixelGraphs]) + }, [graphs, graphsMulti, setPixelGraphs, virtuals]) const handleEvent: GridEventListener<'rowClick'> = (params) => navigate(`/device/${params.row.id}`) @@ -93,67 +219,35 @@ const DbDevices = () => { renderCell: (params: GridRenderCellParams) => ( ) + }, + { + field: 'status', + headerName: 'Status', + width: 200, + align: 'left', + renderCell: (params: GridRenderCellParams) => + devices[Object.keys(devices).find((d) => d === params.row.id) || ''] + ?.online + ? virtuals[params.row.id]?.effect?.name + ? `Effect: ${virtuals[params.row.id]?.effect?.name}` + : 'Online' + : virtuals[params.row.id]?.effect?.name + ? `Effect: ${virtuals[params.row.id]?.effect?.name}` + : 'Offline' + }, + { + field: 'actions', + headerName: 'Actions', + width: 300, // eslint-disable-next-line + renderCell: (params: GridRenderCellParams) => // eslint-disable-next-line + devices[Object.keys(devices).find((d) => d === params.row.id) || '']?.online // eslint-disable-next-line + ? (virtuals[params.row.id]?.effect.name // eslint-disable-next-line + ? () // eslint-disable-next-line + : ()) // eslint-disable-next-line + : virtuals[params.row.id]?.effect.name // eslint-disable-next-line + ? () + : ( activateDevice(params.row.id)} />) } - // { - // field: 'status', - // headerName: 'Status', - // width: 200, - // renderCell: (params: GridRenderCellParams) => - // devices[Object.keys(devices).find((d) => d === params.row.id) || ''] - // ?.online - // ? virtuals[params.row.id]?.effect.name - // ? `Effect: ${virtuals[params.row.id]?.effect.name}` - // : 'Online' - // : 'Offline' - // }, - // { - // field: 'actions', - // headerName: 'Actions', - // width: 300, - // renderCell: (params: GridRenderCellParams) => - // devices[Object.keys(devices).find((d) => d === params.row.id) || ''] - // ?.online ? ( - // virtuals[params.row.id]?.effect.name ? ( - // <> - // - // - // - // ) : ( - // 'Online' - // ) - // ) : ( - // - // ) - // } ] const rows: any = Object.values(virtuals).map((v: any) => ({ diff --git a/src/pages/Home/DbGlobalActions.tsx b/src/pages/Home/DbGlobalActions.tsx index 8b66ca50..cf50ba09 100644 --- a/src/pages/Home/DbGlobalActions.tsx +++ b/src/pages/Home/DbGlobalActions.tsx @@ -1,5 +1,3 @@ -/* eslint-disable no-await-in-loop */ -/* eslint-disable no-plusplus */ import { useTheme, Stack, Box, Button } from '@mui/material' import { useState } from 'react' import BladeFrame from '../../components/SchemaForm/components/BladeFrame' diff --git a/src/pages/Home/DbStats.tsx b/src/pages/Home/DbStats.tsx index 9c94c0ca..65fff5e3 100644 --- a/src/pages/Home/DbStats.tsx +++ b/src/pages/Home/DbStats.tsx @@ -1,37 +1,36 @@ -/* eslint-disable prettier/prettier */ -import { useTheme, Stack } from '@mui/material'; -import BladeFrame from '../../components/SchemaForm/components/BladeFrame'; -import DbRow from './DbRow'; -import useStore from '../../store/useStore'; +import { useTheme, Stack } from '@mui/material' +import BladeFrame from '../../components/SchemaForm/components/BladeFrame' +import DbRow from './DbRow' +import useStore from '../../store/useStore' const DbStats = () => { - const theme = useTheme(); - const config = useStore((state) => state.config); - const devices = useStore((state) => state.devices); - const virtuals = useStore((state) => state.virtuals); - const scenes = useStore((state) => state.scenes); - const devicesOnline = Object.keys(devices).filter((d) => devices[d].online); + const theme = useTheme() + const config = useStore((state) => state.config) + const devices = useStore((state) => state.devices) + const virtuals = useStore((state) => state.virtuals) + const scenes = useStore((state) => state.scenes) + const devicesOnline = Object.keys(devices).filter((d) => devices[d].online) const virtualsReal = Object.keys(virtuals).filter( (d) => !virtuals[d].is_device - ); + ) const pixelTotalOnline = Object.keys(devices) .map((d) => devices[d].online && devices[d].config.pixel_count) - .reduce((a, b) => a + b, 0); + .reduce((a, b) => a + b, 0) const pixelTotal = Object.keys(devices) .map((d) => devices[d].config.pixel_count) - .reduce((a, b) => a + b, 0); + .reduce((a, b) => a + b, 0) return ( @@ -47,20 +46,22 @@ const DbStats = () => { Object.keys(e).length) - .reduce((a: number, b: number) => a + b, 0) - : 0)} + right={String( + Object.values(config.user_presets).length // eslint-disable-next-line + ? Object.values(config.user_presets).map((e: any) => Object.keys(e).length).reduce((a: number, b: number) => a + b, 0) + : 0 + )} /> - ); -}; + ) +} -export default DbStats; +export default DbStats diff --git a/src/pages/Home/Gauge.tsx b/src/pages/Home/Gauge.tsx index d4d01dbb..22c01e2b 100644 --- a/src/pages/Home/Gauge.tsx +++ b/src/pages/Home/Gauge.tsx @@ -1,4 +1,3 @@ -/* eslint-disable prettier/prettier */ import { useTheme } from '@mui/material/styles' import { Box, CircularProgress, Typography, useMediaQuery } from '@mui/material' @@ -8,13 +7,7 @@ export default function Gauge(props: any) { const xSmallHeight = useMediaQuery('(max-height: 730px)') const smallHeight = useMediaQuery('(max-height: 800px)') const mediumHeight = useMediaQuery('(max-height: 900px)') - const size = xSmallHeight - ? 6 - : smallHeight - ? 8 - : mediumHeight - ? 10 - :12 + const size = xSmallHeight ? 6 : smallHeight ? 8 : mediumHeight ? 10 : 12 return ( {current} @@ -63,7 +56,7 @@ export default function Gauge(props: any) { {unit} diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index ef978686..ef928379 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,6 +1,3 @@ -/* eslint-disable no-promise-executor-return */ -/* eslint-disable no-plusplus */ -/* eslint-disable no-await-in-loop */ import { useEffect, useState } from 'react' import { useSnackbar } from 'notistack' @@ -20,7 +17,7 @@ export default function Home() { const scanForDevices = useStore((state) => state.scanForDevices) const getDevices = useStore((state) => state.getDevices) const getVirtuals = useStore((state) => state.getVirtuals) - // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars + const [scanning, setScanning] = useState(-1) const [lastFound, setLastFound] = useState([] as string[]) const features = useStore((state) => state.features) diff --git a/src/pages/Integrations/IntegrationCard/IntegrationCardQLC.tsx b/src/pages/Integrations/IntegrationCard/IntegrationCardQLC.tsx index 4776aad6..9de13516 100644 --- a/src/pages/Integrations/IntegrationCard/IntegrationCardQLC.tsx +++ b/src/pages/Integrations/IntegrationCard/IntegrationCardQLC.tsx @@ -1,7 +1,3 @@ -/* eslint-disable react/jsx-no-undef */ -/* eslint-disable no-console */ -/* eslint-disable @typescript-eslint/indent */ -/* eslint-disable prettier/prettier */ import { useState } from 'react' import Card from '@mui/material/Card' import Button from '@mui/material/Button' @@ -68,12 +64,12 @@ const IntegrationCardQLC = ({ integration }: any) => { integrations[integration].status === 3 ? 'Connecting...' : integrations[integration].status === 2 - ? 'Disconnecting' - : integrations[integration].status === 1 - ? 'Connected' - : integrations[integration].status === 0 - ? 'Disconnected' - : 'Unknown' + ? 'Disconnecting' + : integrations[integration].status === 1 + ? 'Connected' + : integrations[integration].status === 0 + ? 'Disconnected' + : 'Unknown' }`} action={ ) { - return ; -}); + return +}) Transition.defaultProps = { - children:
loading
, -}; + children:
loading
+} export const MuiMenuItem = React.forwardRef( (props: any, ref: React.Ref) => { - return ; + return } -); +) diff --git a/src/pages/Integrations/Spotify/SpotifyLoginRedirect.tsx b/src/pages/Integrations/Spotify/SpotifyLoginRedirect.tsx index 1058fc34..111b8651 100644 --- a/src/pages/Integrations/Spotify/SpotifyLoginRedirect.tsx +++ b/src/pages/Integrations/Spotify/SpotifyLoginRedirect.tsx @@ -1,22 +1,28 @@ -/* eslint-disable no-extra-boolean-cast */ -/* eslint-disable no-return-assign */ -/* eslint-disable prettier/prettier */ import { useState, useEffect } from 'react' -import isElectron from 'is-electron'; -import { Avatar, Dialog, Stack , CircularProgress } from '@mui/material' +import isElectron from 'is-electron' +import { Avatar, Dialog, Stack, CircularProgress } from '@mui/material' import { CheckCircle } from '@mui/icons-material' -import { - finishAuth, - refreshAuth -} from '../../../utils/spotifyProxies' +import { finishAuth, refreshAuth } from '../../../utils/spotifyProxies' import logoAsset from '../../../assets/logo.png' import BladeIcon from '../../../components/Icons/BladeIcon/BladeIcon' +const baseURL = isElectron() + ? 'http://localhost:8888' + : window.location.href.split('/#')[0].replace(/\/+$/, '') || + 'http://localhost:8888' +const storedURL = window.localStorage.getItem('ledfx-host') -const baseURL = isElectron() ? 'http://localhost:8888' : window.location.href.split('/#')[0].replace(/\/+$/, '') || 'http://localhost:8888'; -const storedURL = window.localStorage.getItem('ledfx-host'); - -const Circle = () =>
+const Circle = () => ( +
+) const SpotifyLoginRedirect = () => { const [ready, setReady] = useState(false) @@ -24,49 +30,89 @@ const SpotifyLoginRedirect = () => { useEffect(() => { finishAuth() refreshAuth() - localStorage.setItem('Spotify-Token', window.location.search.replace('?code=', '')) - setTimeout(() => { setReady(true) }, 1500) - setTimeout( () => - (window.location.href = `${ - process.env.NODE_ENV === 'production' - ? storedURL || baseURL - : 'http://localhost:3000' - }/#/Integrations?`), - 3000 + localStorage.setItem( + 'Spotify-Token', + window.location.search.replace('?code=', '') + ) + setTimeout(() => { + setReady(true) + }, 1500) + setTimeout( + () => + (window.location.href = `${ + process.env.NODE_ENV === 'production' + ? storedURL || baseURL + : 'http://localhost:3000' + }/#/Integrations?`), + 3000 ) // Redirect to homepage after 3 sec }, []) - - return ( -
- - - - - - {ready ? ( - - ) : ( - - )} - - - - - +
+ + - - - {ready ? 'Successfully logged in with Spotify...' : 'Logging in with Spotify...'} -
-
- ); + + + + {ready ? ( + + ) : ( + + )} + + + + + + + + {ready + ? 'Successfully logged in with Spotify...' + : 'Logging in with Spotify...'} +
+ + ) } -export default SpotifyLoginRedirect; +export default SpotifyLoginRedirect diff --git a/src/pages/Integrations/Spotify/SpotifyScreen/SpotifyScreen.props.tsx b/src/pages/Integrations/Spotify/SpotifyScreen/SpotifyScreen.props.tsx index 9a044403..236da8ea 100644 --- a/src/pages/Integrations/Spotify/SpotifyScreen/SpotifyScreen.props.tsx +++ b/src/pages/Integrations/Spotify/SpotifyScreen/SpotifyScreen.props.tsx @@ -1,33 +1,19 @@ import React from 'react' -import { Settings } from '@mui/icons-material' import { TransitionProps } from '@mui/material/transitions' import { MenuItem, Slide } from '@mui/material' export interface SpotifyScreenProps { icon: React.ReactElement - startIcon: React.ReactElement - label: string - type: string + startIcon?: React.ReactElement + label?: string + type?: string className: string - // eslint-disable-next-line prettier/prettier + // eslint-disable-next-line color: 'primary' | 'inherit' | 'error' | 'success' | 'warning' | 'info' | 'secondary' | undefined; variant: 'outlined' | 'text' | 'contained' | undefined - innerKey: string + innerKey?: string disabled: boolean - size: 'small' | 'medium' | 'large' | undefined -} - -export const SpotifyScreenDefaultProps = { - icon: , - startIcon: undefined, - label: '', - type: 'button', - className: undefined, - color: 'primary', - variant: 'contained', - innerKey: undefined, - disabled: false, - size: 'small' + size?: 'small' | 'medium' | 'large' | undefined } export const Transition = React.forwardRef(function Transition( @@ -37,10 +23,6 @@ export const Transition = React.forwardRef(function Transition( return }) -Transition.defaultProps = { - children:
loading
-} - export const MuiMenuItem = React.forwardRef( (props: any, ref: React.Ref) => { return diff --git a/src/pages/Integrations/Spotify/SpotifyScreen/SpotifyScreen.tsx b/src/pages/Integrations/Spotify/SpotifyScreen/SpotifyScreen.tsx index 8fe74814..7baa5806 100644 --- a/src/pages/Integrations/Spotify/SpotifyScreen/SpotifyScreen.tsx +++ b/src/pages/Integrations/Spotify/SpotifyScreen/SpotifyScreen.tsx @@ -13,7 +13,6 @@ import { Settings, NavigateBefore } from '@mui/icons-material' import isElectron from 'is-electron' import { MuiMenuItem, - SpotifyScreenDefaultProps, SpotifyScreenProps, Transition } from './SpotifyScreen.props' @@ -26,14 +25,14 @@ import SpPlaylist from '../../../../components/Integrations/Spotify/Widgets/Spot import { SpotifyStateContext } from '../../../../components/Integrations/Spotify/SpotifyProvider' export default function SpotifyScreen({ + className, + innerKey, icon = , startIcon, label = '', - type, - className, + type = 'button', color = 'primary', variant = 'contained', - innerKey, disabled = false, size = 'small' }: SpotifyScreenProps) { @@ -125,5 +124,3 @@ export default function SpotifyScreen({ ) } - -SpotifyScreen.defaultProps = SpotifyScreenDefaultProps diff --git a/src/pages/Login/LoginRedirect.tsx b/src/pages/Login/LoginRedirect.tsx index 022424d9..959f323a 100644 --- a/src/pages/Login/LoginRedirect.tsx +++ b/src/pages/Login/LoginRedirect.tsx @@ -51,7 +51,6 @@ const LoginRedirect = () => { }, 2000) }) .catch((err) => { - // eslint-disable-next-line no-console console.log(err) setText('An error occurred, please see the developer console.') }) diff --git a/src/pages/Pages.tsx b/src/pages/Pages.tsx index eb9e8b02..bed796db 100644 --- a/src/pages/Pages.tsx +++ b/src/pages/Pages.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-no-useless-fragment */ import { HashRouter as Router, BrowserRouter, @@ -62,7 +61,7 @@ const Routings = ({ handleWs }: any) => { window.location.reload() }) if (isElect) { - useHotkeys(['ctrl+alt+l'], () => { + useHotkeys(['ctrl+alt+l'], () => { // eslint-disable-line window.localStorage.setItem('lock', 'activated') window.location.reload() }) @@ -133,7 +132,7 @@ const Routings = ({ handleWs }: any) => { : } /> diff --git a/src/pages/Scenes/ScenesPlaylist.tsx b/src/pages/Scenes/ScenesPlaylist.tsx index c393c45d..d410ac2a 100644 --- a/src/pages/Scenes/ScenesPlaylist.tsx +++ b/src/pages/Scenes/ScenesPlaylist.tsx @@ -106,7 +106,7 @@ export default function ScenesPlaylist({ headerName: 'Remove', width: 120, renderCell: (params: GridRenderCellParams) => { - const removeScene2PL = useStore((state) => state.removeScene2PL) + const removeScene2PL = useStore((state) => state.removeScene2PL) // eslint-disable-line return (
) } -SettingsRow.defaultProps = { - step: 'x', - children: null, - value: null, - checked: false, - direct: false, - onChange: null, - style: null, - disabled: false -} - export const SettingsAccordion = ({ title, accId, @@ -276,7 +254,7 @@ export const SettingsAccordion = ({ title: string accId: string children: any - // eslint-disable-next-line react/require-default-props + icon?: string }) => { const settingsExpanded = useStore((state) => state.ui.settingsExpanded) diff --git a/src/pages/Settings/Webaudio.tsx b/src/pages/Settings/Webaudio.tsx index 12417327..d9d17297 100644 --- a/src/pages/Settings/Webaudio.tsx +++ b/src/pages/Settings/Webaudio.tsx @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/indent */ -/* eslint-disable no-console */ -/* eslint-disable no-plusplus */ -/* eslint-disable func-names */ + import { Button, Fab, diff --git a/src/pages/User/AvatarPicker/AvatarPicker.interface.tsx b/src/pages/User/AvatarPicker/AvatarPicker.interface.tsx index d67009e6..a8b8a71f 100644 --- a/src/pages/User/AvatarPicker/AvatarPicker.interface.tsx +++ b/src/pages/User/AvatarPicker/AvatarPicker.interface.tsx @@ -1,5 +1,3 @@ -import { Edit, GitHub } from '@mui/icons-material' - const IStorage = ['localStorage', 'indexedDb', 'cloud', 'custom'] as const export const storageOptions = [ 'localStorage', @@ -69,38 +67,3 @@ export interface AvatarPickerProps { */ props?: any } - -export const AvatarPickerDefaults: AvatarPickerProps = { - size: 150, - defaultIcon: , - editIcon: ( - - ), - avatar: undefined, - initialZoom: 1, - minZoom: 0.01, - maxZoom: 3, - stepZoom: 0.01, - minRotation: 0, - maxRotation: 360, - stepRotation: 0.01, - setAvatar: null, - storage: 'indexedDb', - storageKey: 'avatar', - props: {} -} diff --git a/src/pages/User/AvatarPicker/AvatarPicker.tsx b/src/pages/User/AvatarPicker/AvatarPicker.tsx index 5289bc48..16741e21 100644 --- a/src/pages/User/AvatarPicker/AvatarPicker.tsx +++ b/src/pages/User/AvatarPicker/AvatarPicker.tsx @@ -1,7 +1,6 @@ -/* eslint-disable no-console */ import { useEffect, useRef, useState } from 'react' import Cropper, { Area } from 'react-easy-crop' -import { Delete, Save, UploadFile } from '@mui/icons-material' +import { Delete, Edit, GitHub, Save, UploadFile } from '@mui/icons-material' import setupIndexedDB, { useIndexedDBStore } from 'use-indexeddb' import { Avatar, @@ -17,19 +16,31 @@ import { Typography } from '@mui/material' import { getCroppedImg, idbConfig, readFile } from './avatarUtils' -import { - AvatarPickerDefaults, - AvatarPickerProps - // storageOptions -} from './AvatarPicker.interface' +import { AvatarPickerProps } from './AvatarPicker.interface' import { backendUrl, cloud } from '../../Device/Cloud/CloudComponents' const AvatarPicker = ({ - defaultIcon, - editIcon, - avatar, - setAvatar, size = 150, + defaultIcon = , + editIcon = ( + + ), + avatar = undefined, initialZoom = 1, minZoom = 0.01, maxZoom = 3, @@ -37,14 +48,15 @@ const AvatarPicker = ({ minRotation = 0, maxRotation = 360, stepRotation = 0.01, - storage = 'cloud', + setAvatar = null, + storage = 'indexedDb', storageKey = 'avatar', ...props }: AvatarPickerProps) => { const inputRef = useRef(null) const [open, setOpen] = useState(false) - // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars - const [newStorage, setNewStorage] = useState(storage) + + const [newStorage] = useState(storage) const [imageSrc, setImageSrc] = useState(null) const [crop, setCrop] = useState({ x: 0, y: 0 }) const [rotation, setRotation] = useState(0) @@ -63,12 +75,11 @@ const AvatarPicker = ({ `user-details?user.username=${localStorage.getItem('username')}` ) if (response.status !== 200) { - // eslint-disable-next-line no-alert alert('No Access') return } const res = await response.data - // eslint-disable-next-line consistent-return + return res } catch (e) { console.error(e) @@ -101,7 +112,7 @@ const AvatarPicker = ({ }, []) const { getByID, update } = - newStorage === 'indexedDb' && !setAvatar + newStorage === 'indexedDb' && !setAvatar // eslint-disable-next-line ? useIndexedDBStore('avatars') : { getByID: null, update: null } @@ -139,7 +150,7 @@ const AvatarPicker = ({ .then((res) => res.blob()) .then((blob) => { const reader = new FileReader() - // eslint-disable-next-line func-names + reader.onloadend = async function () { if (reader.result) { if (newStorage === 'custom' && setAvatar) { @@ -397,6 +408,4 @@ const AvatarPicker = ({ ) } -AvatarPicker.defaultProps = AvatarPickerDefaults - export default AvatarPicker diff --git a/src/pages/User/User.tsx b/src/pages/User/User.tsx index 7cf993af..889f8883 100644 --- a/src/pages/User/User.tsx +++ b/src/pages/User/User.tsx @@ -1,7 +1,5 @@ -/* eslint-disable no-console */ -/* eslint-disable @typescript-eslint/indent */ /* eslint-disable no-self-assign */ -/* eslint-disable no-alert */ + import { Badge, Box, diff --git a/src/reportWebVitals.ts b/src/reportWebVitals.ts index 57a24a21..88951fb3 100644 --- a/src/reportWebVitals.ts +++ b/src/reportWebVitals.ts @@ -1,13 +1,13 @@ -import { ReportHandler } from 'web-vitals' +import { MetricType } from 'web-vitals' -const reportWebVitals = (onPerfEntry?: ReportHandler) => { +const reportWebVitals = (onPerfEntry?: (_metric: MetricType) => void) => { if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry) - getFID(onPerfEntry) - getFCP(onPerfEntry) - getLCP(onPerfEntry) - getTTFB(onPerfEntry) + import('web-vitals').then(({ onCLS, onFID, onFCP, onLCP, onTTFB }) => { + onCLS(onPerfEntry) + onFID(onPerfEntry) + onFCP(onPerfEntry) + onLCP(onPerfEntry) + onTTFB(onPerfEntry) }) } } diff --git a/src/service-worker.ts b/src/service-worker.ts index f16f5257..e92c4cf6 100644 --- a/src/service-worker.ts +++ b/src/service-worker.ts @@ -1,5 +1,4 @@ /// -/* eslint-disable no-restricted-globals */ // This service worker can be customized! // See https://developers.google.com/web/tools/workbox/modules @@ -22,7 +21,7 @@ clientsClaim() // Their URLs are injected into the manifest variable below. // This variable must be present somewhere in your service worker file, // even if you decide not to use precaching. See https://cra.link/PWA -// eslint-disable-next-line no-underscore-dangle + precacheAndRoute(self.__WB_MANIFEST) // Set up App Shell-style routing, so that all navigation requests diff --git a/src/serviceWorkerRegistration.ts b/src/serviceWorkerRegistration.ts index 28234803..d2bc0440 100644 --- a/src/serviceWorkerRegistration.ts +++ b/src/serviceWorkerRegistration.ts @@ -1,6 +1,3 @@ -/* eslint-disable no-unused-vars */ -/* eslint-disable no-param-reassign */ -/* eslint-disable no-console */ // This optional code is used to register a service worker. // register() is not called by default. @@ -14,8 +11,8 @@ // opt-in, read https://cra.link/PWA type Config = { - onSuccess?: (registration: ServiceWorkerRegistration) => void - onUpdate?: (registration: ServiceWorkerRegistration) => void + onSuccess?: (_registration: ServiceWorkerRegistration) => void + onUpdate?: (_registration: ServiceWorkerRegistration) => void } const isLocalhost = Boolean( diff --git a/src/store/api/storeActions.tsx b/src/store/api/storeActions.tsx index 3ac00264..fcc73446 100644 --- a/src/store/api/storeActions.tsx +++ b/src/store/api/storeActions.tsx @@ -1,8 +1,3 @@ -/* eslint-disable prettier/prettier */ -/* eslint-disable @typescript-eslint/indent */ -/* eslint-disable no-return-await */ -/* eslint-disable no-param-reassign */ -/* eslint-disable import/no-cycle */ import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IStore, IOpenRgbDevice } from '../useStore' @@ -25,23 +20,33 @@ const storeActions = (set: any) => ({ type: 'openrgb', config: { icon_name: - d.type === 0 ? 'mdi:chip' - : d.type === 2 ? 'mdi:expansion-card-variant' - : d.type === 5 ? 'mdi:keyboard' - : d.type === 6 ? (d.name.includes('Razer') ? 'razer:mouse' : 'mouse') - : d.type === 8 ? 'mdi:headphones' - : d.type === 9 ? 'mdi:headphones-bluetooth' - : d.type === 10 ? 'sportsEsports' - : d.type === 12 ? 'mdi:speaker-wireless' - : 'mdi:led-strip', + d.type === 0 + ? 'mdi:chip' + : d.type === 2 + ? 'mdi:expansion-card-variant' + : d.type === 5 + ? 'mdi:keyboard' + : d.type === 6 + ? d.name.includes('Razer') + ? 'razer:mouse' + : 'mouse' + : d.type === 8 + ? 'mdi:headphones' + : d.type === 9 + ? 'mdi:headphones-bluetooth' + : d.type === 10 + ? 'sportsEsports' + : d.type === 12 + ? 'mdi:speaker-wireless' + : 'mdi:led-strip', center_offset: 0, refresh_rate: 64, openrgb_id: d.id, pixel_count: d.leds, port: 6742, name: d.name, - ip_address: '127.0.0.1', - }, + ip_address: '127.0.0.1' + } }) ) } @@ -65,8 +70,8 @@ const storeActions = (set: any) => ({ rows: resp.device.rows, icon_name: 'launchpad', create_segments: resp.device.name === 'Launchpad X', - name: resp.device.name, - }, + name: resp.device.name + } }) } return false @@ -103,22 +108,25 @@ const storeActions = (set: any) => ({ shutdown: async () => await Ledfx('/api/power', 'POST', { timeout: 0, - action: 'shutdown', + action: 'shutdown' }), restart: async () => await Ledfx('/api/power', 'POST', { timeout: 0, - action: 'restart', + action: 'restart' }), getInfo: async () => await Ledfx('/api/info'), - getUpdateInfo: async (snackbar: boolean) => await Ledfx('/api/check_for_updates', 'GET', {}, snackbar), + getUpdateInfo: async (snackbar: boolean) => + await Ledfx('/api/check_for_updates', 'GET', {}, snackbar), getPing: async (virtId: string) => await Ledfx(`/api/ping/${virtId}`), - getImage: async (path_url: string) => await Ledfx('/api/get_image', 'POST', { - path_url - }), - getGifFrames: async (path_url: string) => await Ledfx('/api/get_gif_frames', 'POST', { - path_url - }), + getImage: async (path_url: string) => + await Ledfx('/api/get_image', 'POST', { + path_url + }), + getGifFrames: async (path_url: string) => + await Ledfx('/api/get_gif_frames', 'POST', { + path_url + }) }) export default storeActions diff --git a/src/store/api/storeColors.tsx b/src/store/api/storeColors.tsx index 689d59e4..a5a8c438 100644 --- a/src/store/api/storeColors.tsx +++ b/src/store/api/storeColors.tsx @@ -1,6 +1,3 @@ -/* eslint-disable no-return-await */ -/* eslint-disable no-param-reassign */ -/* eslint-disable import/no-cycle */ import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IStore } from '../useStore' diff --git a/src/store/api/storeConfig.tsx b/src/store/api/storeConfig.tsx index 9483e61b..344fb514 100644 --- a/src/store/api/storeConfig.tsx +++ b/src/store/api/storeConfig.tsx @@ -1,6 +1,3 @@ -/* eslint-disable no-return-await */ -/* eslint-disable no-param-reassign */ -/* eslint-disable import/no-cycle */ import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IStore } from '../useStore' diff --git a/src/store/api/storeDevices.tsx b/src/store/api/storeDevices.tsx index bcfc150d..2f7979bf 100644 --- a/src/store/api/storeDevices.tsx +++ b/src/store/api/storeDevices.tsx @@ -1,6 +1,3 @@ -/* eslint-disable no-return-await */ -/* eslint-disable no-param-reassign */ -/* eslint-disable import/no-cycle */ import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IStore, IOpenRgbDevice } from '../useStore' diff --git a/src/store/api/storeIntegrations.tsx b/src/store/api/storeIntegrations.tsx index 39536192..b68bd278 100644 --- a/src/store/api/storeIntegrations.tsx +++ b/src/store/api/storeIntegrations.tsx @@ -1,6 +1,3 @@ -/* eslint-disable no-return-await */ -/* eslint-disable no-param-reassign */ -/* eslint-disable import/no-cycle */ import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IStore } from '../useStore' diff --git a/src/store/api/storeIntegrationsSpotify.tsx b/src/store/api/storeIntegrationsSpotify.tsx index df73a9d9..9d110e76 100644 --- a/src/store/api/storeIntegrationsSpotify.tsx +++ b/src/store/api/storeIntegrationsSpotify.tsx @@ -1,5 +1,3 @@ -/* eslint-disable import/no-cycle */ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IStore } from '../useStore' diff --git a/src/store/api/storePresets.tsx b/src/store/api/storePresets.tsx index 03d1517b..dd96be81 100644 --- a/src/store/api/storePresets.tsx +++ b/src/store/api/storePresets.tsx @@ -1,6 +1,3 @@ -/* eslint-disable no-return-await */ -/* eslint-disable no-param-reassign */ -/* eslint-disable import/no-cycle */ import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IPresets } from './storeConfig' diff --git a/src/store/api/storeScenes.tsx b/src/store/api/storeScenes.tsx index e2d085ac..ace37e48 100644 --- a/src/store/api/storeScenes.tsx +++ b/src/store/api/storeScenes.tsx @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/indent */ -/* eslint-disable no-return-await */ -/* eslint-disable no-param-reassign */ -/* eslint-disable import/no-cycle */ + import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IStore } from '../useStore' diff --git a/src/store/api/storeVirtuals.tsx b/src/store/api/storeVirtuals.tsx index 62b3c088..a2e1c4d8 100644 --- a/src/store/api/storeVirtuals.tsx +++ b/src/store/api/storeVirtuals.tsx @@ -1,6 +1,3 @@ -/* eslint-disable no-return-await */ -/* eslint-disable no-param-reassign */ -/* eslint-disable import/no-cycle */ import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IStore } from '../useStore' diff --git a/src/store/ui/storeCloud.tsx b/src/store/ui/storeCloud.tsx index 0b05e0fd..31e4dedc 100644 --- a/src/store/ui/storeCloud.tsx +++ b/src/store/ui/storeCloud.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import type { IStore } from '../useStore' diff --git a/src/store/ui/storeDialogs.tsx b/src/store/ui/storeDialogs.tsx index 7ef78bd6..1c072fec 100644 --- a/src/store/ui/storeDialogs.tsx +++ b/src/store/ui/storeDialogs.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import type { IStore } from '../useStore' diff --git a/src/store/ui/storeFeatures.tsx b/src/store/ui/storeFeatures.tsx index 0e4ca6ac..459281a0 100644 --- a/src/store/ui/storeFeatures.tsx +++ b/src/store/ui/storeFeatures.tsx @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/indent */ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import type { IStore } from '../useStore' @@ -27,6 +25,7 @@ export type IFeatures = | 'scenechips' | 'alpha' | 'matrix' + | 'matrix_cam' | 'mqtt' | 'mqtt_hass' | 'gamepad' @@ -57,7 +56,8 @@ const storeFeatures = (set: any) => ({ matrix: false, mqtt: false, mqtt_hass: false, - gamepad: false + gamepad: false, + matrix_cam: false }, showFeatures: { dev: false, @@ -85,7 +85,8 @@ const storeFeatures = (set: any) => ({ matrix: false, mqtt: false, mqtt_hass: false, - gamepad: false + gamepad: false, + matrix_cam: false }, setFeatures: (feat: IFeatures, use: boolean): void => set( diff --git a/src/store/ui/storeGeneral.tsx b/src/store/ui/storeGeneral.tsx index a884fa9c..0bab9b1a 100644 --- a/src/store/ui/storeGeneral.tsx +++ b/src/store/ui/storeGeneral.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import isElectron from 'is-electron' import type { IStore } from '../useStore' diff --git a/src/store/ui/storeNotifications.tsx b/src/store/ui/storeNotifications.tsx index 0060f1bf..0106d2f7 100644 --- a/src/store/ui/storeNotifications.tsx +++ b/src/store/ui/storeNotifications.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import type { IStore } from '../useStore' diff --git a/src/store/ui/storePad.tsx b/src/store/ui/storePad.tsx index c40eca3a..214d1f8e 100644 --- a/src/store/ui/storePad.tsx +++ b/src/store/ui/storePad.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import type { IStore } from '../useStore' diff --git a/src/store/ui/storeQLCActions.tsx b/src/store/ui/storeQLCActions.tsx index 28242baf..742ee059 100644 --- a/src/store/ui/storeQLCActions.tsx +++ b/src/store/ui/storeQLCActions.tsx @@ -1,6 +1,3 @@ -/* eslint-disable import/no-cycle */ -/* eslint-disable no-param-reassign */ - import { produce } from 'immer' import { Ledfx } from '../../api/ledfx' import type { IStore } from '../useStore' diff --git a/src/store/ui/storeSpotifyActions.tsx b/src/store/ui/storeSpotifyActions.tsx index 10753aa1..0ae43fe9 100644 --- a/src/store/ui/storeSpotifyActions.tsx +++ b/src/store/ui/storeSpotifyActions.tsx @@ -1,5 +1,3 @@ -/* eslint-disable default-case */ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import { SpotifyState } from './SpotifyState' import type { IStore } from '../useStore' diff --git a/src/store/ui/storeTours.tsx b/src/store/ui/storeTours.tsx index 994e0fa2..137367c9 100644 --- a/src/store/ui/storeTours.tsx +++ b/src/store/ui/storeTours.tsx @@ -1,8 +1,13 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import type { IStore } from '../useStore' -// eslint-disable-next-line prettier/prettier -type ITours = 'home' | 'devices' | 'device' | 'effect' | 'integrations' | 'scenes' | 'settings'; +type ITours = + | 'home' + | 'devices' + | 'device' + | 'effect' + | 'integrations' + | 'scenes' + | 'settings' const storeTours = (set: any) => ({ tours: { diff --git a/src/store/ui/storeUI.tsx b/src/store/ui/storeUI.tsx index 82c1b603..3e0fa7de 100644 --- a/src/store/ui/storeUI.tsx +++ b/src/store/ui/storeUI.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import { VariantType } from 'notistack' import pkg from '../../../package.json' @@ -55,10 +54,11 @@ const storeUI = (set: any) => ({ scenes: true, devices: true, user: true, - gamepad: true + gamepad: true, + matrix: true }, setInfoAlerts: ( - key: 'scenes' | 'devices' | 'user' | 'gamepad', + key: 'scenes' | 'devices' | 'user' | 'gamepad' | 'matrix', val: boolean ): void => set( diff --git a/src/store/ui/storeUser.tsx b/src/store/ui/storeUser.tsx index 71037e14..b46f27dd 100644 --- a/src/store/ui/storeUser.tsx +++ b/src/store/ui/storeUser.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import type { IStore } from '../useStore' diff --git a/src/store/ui/storeVideo.tsx b/src/store/ui/storeVideo.tsx new file mode 100644 index 00000000..c3c90d32 --- /dev/null +++ b/src/store/ui/storeVideo.tsx @@ -0,0 +1,52 @@ +import { produce } from 'immer' +import type { IStore } from '../useStore' + +export interface IPoints { + x: number + y: number + device: string + led: number + segment: string +} + +const storeVideo = (set: any) => ({ + videoMapper: { + wledIp: '', + calibrating: false + }, + points: [] as IPoints[], + setPoints: (points: IPoints[]): void => + set( + produce((state: IStore) => { + state.points = points + }), + false, + 'setPoints' + ), + addPoint: (point: IPoints): void => + set( + produce((state: IStore) => { + state.points.push(point) + }), + false, + 'addPoint' + ), + setWledIp: (wledIp: string): void => + set( + produce((state: IStore) => { + state.videoMapper.wledIp = wledIp + }), + false, + 'setWledIp' + ), + setCalibrating: (calibrating: boolean): void => + set( + produce((state: IStore) => { + state.videoMapper.calibrating = calibrating + }), + false, + 'setCalibrating' + ) +}) + +export default storeVideo diff --git a/src/store/ui/storeWebAudio.tsx b/src/store/ui/storeWebAudio.tsx index afba347b..c8d7223f 100644 --- a/src/store/ui/storeWebAudio.tsx +++ b/src/store/ui/storeWebAudio.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' import type { IStore } from '../useStore' diff --git a/src/store/ui/storeYoutube.tsx b/src/store/ui/storeYoutube.tsx index 63dfa07e..67a026f1 100644 --- a/src/store/ui/storeYoutube.tsx +++ b/src/store/ui/storeYoutube.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { produce } from 'immer' const storeYoutube = (set: any) => ({ diff --git a/src/store/useStore.ts b/src/store/useStore.ts index fda06b6d..61380a46 100644 --- a/src/store/useStore.ts +++ b/src/store/useStore.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-cycle */ import { create } from 'zustand' import { devtools, combine, persist } from 'zustand/middleware' @@ -26,6 +25,7 @@ import storeSpotifyActions from './ui/storeSpotifyActions' import storeQLCActions from './ui/storeQLCActions' import storeNotifications from './ui/storeNotifications' import storePad from './ui/storePad' +import storeVideo from './ui/storeVideo' const useStore = create( devtools( @@ -40,6 +40,7 @@ const useStore = create( qlc: storeQLC(), user: storeUser(set), ...storePad(set), + ...storeVideo(set), ...storeNotifications(set), ...storeTours(set), ...storeSpotifyActions(set), diff --git a/src/stories/AppStructure.stories.mdx b/src/stories/AppStructure.mdx similarity index 63% rename from src/stories/AppStructure.stories.mdx rename to src/stories/AppStructure.mdx index c505a233..abbe33c6 100644 --- a/src/stories/AppStructure.stories.mdx +++ b/src/stories/AppStructure.mdx @@ -1,4 +1,4 @@ -import { Meta } from '@storybook/addon-docs'; +import { Meta } from '@storybook/blocks'; import appStructure from './assets/src.png'; import component from './assets/component.png'; import componentss from './assets/components.png'; @@ -9,11 +9,12 @@ import pages from './assets/pages.png'; ### Welcome to Blade's Storybook + # Frontend Development for LedFx This is a growing place for supporting the frontend development of LedFx @@ -40,6 +41,6 @@ This is a growing place for supporting the frontend development of LedFx [GettingStarted](/story/bladebook-getting-started--page) ---- +*** -![state](https://img.shields.io/badge/STATE-beta-blue.svg?logo=github&logoColor=white) ![version](https://img.shields.io/github/v/release/YeonV/LedFx-Frontend-v2?label=VERSION&logo=git&logoColor=white) [![creator](https://img.shields.io/badge/CREATOR-Yeon-blue.svg?logo=github&logoColor=white)](https://github.com/YeonV) [![creator](https://img.shields.io/badge/A.K.A-Blade-darkred.svg?logo=github&logoColor=white)](https://github.com/YeonV) +![state](https://img.shields.io/badge/STATE-beta-blue.svg?logo=github\&logoColor=white) ![version](https://img.shields.io/github/v/release/YeonV/LedFx-Frontend-v2?label=VERSION\&logo=git\&logoColor=white) [![creator](https://img.shields.io/badge/CREATOR-Yeon-blue.svg?logo=github\&logoColor=white)](https://github.com/YeonV) [![creator](https://img.shields.io/badge/A.K.A-Blade-darkred.svg?logo=github\&logoColor=white)](https://github.com/YeonV) diff --git a/src/stories/GettingStarted.stories.mdx b/src/stories/GettingStarted.mdx similarity index 60% rename from src/stories/GettingStarted.stories.mdx rename to src/stories/GettingStarted.mdx index bcc8ae88..3d53c53d 100644 --- a/src/stories/GettingStarted.stories.mdx +++ b/src/stories/GettingStarted.mdx @@ -1,26 +1,28 @@ -import { Meta } from '@storybook/addon-docs'; +import { Meta } from '@storybook/blocks'; ### Welcome to Blade's Storybook + # Frontend Development for LedFx This is a growing place for supporting the frontend development of LedFx ## Requirements -- Running LedFx Core -- Node >= 14 +* Running LedFx Core +* Node >= 14 ## Getting started `yarn` is recommended, but you can use `npm` aswell + ```bash git clone https://github.com/YeonV/LedFx-Frontend-v2 cd LedFx-Frontend-v2 @@ -29,17 +31,19 @@ yarn start # npm run start ``` When working with VsCode, you can make use of tasks: + ```bash git clone https://github.com/YeonV/LedFx-Frontend-v2 cd LedFx-Frontend-v2 code . ``` + `CTRL`+`SHIFT`+`P` -> Run Task
-> Init Workspace (this does the yarn install)
-> Start Frontend (yarn start) [App Structure](/story/bladebook-app-structure--page) ---- +*** -![state](https://img.shields.io/badge/STATE-beta-blue.svg?logo=github&logoColor=white) ![version](https://img.shields.io/github/v/release/YeonV/LedFx-Frontend-v2?label=VERSION&logo=git&logoColor=white) [![creator](https://img.shields.io/badge/CREATOR-Yeon-blue.svg?logo=github&logoColor=white)](https://github.com/YeonV) [![creator](https://img.shields.io/badge/A.K.A-Blade-darkred.svg?logo=github&logoColor=white)](https://github.com/YeonV) +![state](https://img.shields.io/badge/STATE-beta-blue.svg?logo=github\&logoColor=white) ![version](https://img.shields.io/github/v/release/YeonV/LedFx-Frontend-v2?label=VERSION\&logo=git\&logoColor=white) [![creator](https://img.shields.io/badge/CREATOR-Yeon-blue.svg?logo=github\&logoColor=white)](https://github.com/YeonV) [![creator](https://img.shields.io/badge/A.K.A-Blade-darkred.svg?logo=github\&logoColor=white)](https://github.com/YeonV) diff --git a/src/stories/Guides.mdx b/src/stories/Guides.mdx new file mode 100644 index 00000000..5c5f8c27 --- /dev/null +++ b/src/stories/Guides.mdx @@ -0,0 +1,7 @@ +import { Meta } from '@storybook/blocks'; + + + +# How to create tours + +Explain how to create tours diff --git a/src/stories/Guides.stories.mdx b/src/stories/Guides.stories.mdx deleted file mode 100644 index 5a06a96d..00000000 --- a/src/stories/Guides.stories.mdx +++ /dev/null @@ -1,7 +0,0 @@ -import { Meta } from '@storybook/addon-docs'; - - - -# How to create tours - -Explain how to create tours \ No newline at end of file diff --git a/src/stories/Introduction.stories.mdx b/src/stories/Introduction.mdx similarity index 54% rename from src/stories/Introduction.stories.mdx rename to src/stories/Introduction.mdx index 5e19a613..b15434cd 100644 --- a/src/stories/Introduction.stories.mdx +++ b/src/stories/Introduction.mdx @@ -1,16 +1,16 @@ -import { Meta } from '@storybook/addon-docs'; +import { Meta } from '@storybook/blocks'; ### Welcome to Blade's Storybook -# Frontend Development for LedFx +# Frontend Development for LedFx This is a growing place for supporting the frontend development of LedFx @@ -28,6 +28,6 @@ Some nice notes [GettingStarted](/story/bladebook-getting-started--page) ---- +*** -![state](https://img.shields.io/badge/STATE-beta-blue.svg?logo=github&logoColor=white) ![version](https://img.shields.io/github/v/release/YeonV/LedFx-Frontend-v2?label=VERSION&logo=git&logoColor=white) [![creator](https://img.shields.io/badge/CREATOR-Yeon-blue.svg?logo=github&logoColor=white)](https://github.com/YeonV) [![creator](https://img.shields.io/badge/A.K.A-Blade-darkred.svg?logo=github&logoColor=white)](https://github.com/YeonV) +![state](https://img.shields.io/badge/STATE-beta-blue.svg?logo=github\&logoColor=white) ![version](https://img.shields.io/github/v/release/YeonV/LedFx-Frontend-v2?label=VERSION\&logo=git\&logoColor=white) [![creator](https://img.shields.io/badge/CREATOR-Yeon-blue.svg?logo=github\&logoColor=white)](https://github.com/YeonV) [![creator](https://img.shields.io/badge/A.K.A-Blade-darkred.svg?logo=github\&logoColor=white)](https://github.com/YeonV) diff --git a/src/themes/AppThemes.tsx b/src/themes/AppThemes.tsx index 252e2261..ed3fe6ef 100644 --- a/src/themes/AppThemes.tsx +++ b/src/themes/AppThemes.tsx @@ -1,4 +1,3 @@ -/* eslint-disable prettier/prettier */ import { PaletteMode } from '@mui/material' import { createTheme, Theme } from '@mui/material/styles' import isElectron from 'is-electron' @@ -21,8 +20,15 @@ export const common = { components: { MuiButton: { defaultProps: { - // eslint-disable-next-line prettier/prettier - color: 'inherit' as 'error' | 'success' | 'warning' | 'info' | 'inherit' | 'primary' | 'secondary' | undefined, + color: 'inherit' as + | 'error' + | 'success' + | 'warning' + | 'info' + | 'inherit' + | 'primary' + | 'secondary' + | undefined, variant: 'outlined' as 'contained' | 'outlined' | 'text' | undefined, size: 'small' as 'small' | 'medium' | 'large' }, @@ -240,15 +246,13 @@ export const ledfxThemes = { DarkPink: BladeDarkPinkTheme } as any -/* eslint-disable @typescript-eslint/indent */ export const ledfxTheme = (window.localStorage.getItem('ledfx-theme') ? window.localStorage.getItem('ledfx-theme') : window.localStorage.getItem('hassTokens') - ? 'DarkBlue' - : window.location.origin === 'https://my.ledfx.app' - ? 'DarkGreen' - : isElectron() - ? 'DarkOrange' - : 'DarkBlue') || 'DarkBlue' -/* eslint-enable @typescript-eslint/indent */ + ? 'DarkBlue' + : window.location.origin === 'https://my.ledfx.app' + ? 'DarkGreen' + : isElectron() + ? 'DarkOrange' + : 'DarkBlue') || 'DarkBlue' diff --git a/src/utils/ErrorBoundary.tsx b/src/utils/ErrorBoundary.tsx index 04bd61ef..aba0f76a 100644 --- a/src/utils/ErrorBoundary.tsx +++ b/src/utils/ErrorBoundary.tsx @@ -1,6 +1,4 @@ -/* eslint-disable react/require-default-props */ -/* eslint-disable react/destructuring-assignment */ -import React, { Component, ErrorInfo, ReactNode } from 'react' +import { Component, ErrorInfo, ReactNode } from 'react' interface Props { children?: ReactNode @@ -21,7 +19,6 @@ class ErrorBoundary extends Component { } componentDidCatch(error: Error, errorInfo: ErrorInfo) { - // eslint-disable-next-line no-console console.warn(error, errorInfo) } diff --git a/src/utils/Websocket.ts b/src/utils/Websocket.ts index 1027d278..5ad9b253 100644 --- a/src/utils/Websocket.ts +++ b/src/utils/Websocket.ts @@ -1,8 +1,5 @@ -/* eslint-disable no-use-before-define */ -/* eslint-disable consistent-return */ -/* eslint-disable no-underscore-dangle */ /* eslint-disable array-callback-return */ -/* eslint-disable no-plusplus */ + import React, { useEffect, useLayoutEffect, useState } from 'react' import { useLocation } from 'react-router-dom' diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 9be5aff6..3ddb3ca3 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -96,7 +96,7 @@ export const initFrontendConfig = () => { export const log = (...props: any[]) => { if (typeof props[0] === 'string') { - // eslint-disable-next-line no-console + console.log( `%c ${props[0] .replace('success', '') @@ -117,7 +117,7 @@ export const log = (...props: any[]) => { } export const sleep = (ms: number) => { - // eslint-disable-next-line no-promise-executor-return + return new Promise((resolve) => setTimeout(resolve, ms)) } @@ -134,7 +134,7 @@ export const ordered = (unordered: Record) => Object.keys(unordered) .sort() .reduce((obj: any, key) => { - // eslint-disable-next-line no-param-reassign + obj[key] = unordered[key] return obj }, {}) diff --git a/src/utils/login.ts b/src/utils/login.ts index 01446dd8..fd134181 100644 --- a/src/utils/login.ts +++ b/src/utils/login.ts @@ -30,7 +30,6 @@ const login = async (search: string) => { // }, 2000) }) .catch((err) => { - // eslint-disable-next-line no-console console.log(err) }) } diff --git a/src/utils/spotifyProxies.ts b/src/utils/spotifyProxies.ts index 5b45317c..0ed42c61 100644 --- a/src/utils/spotifyProxies.ts +++ b/src/utils/spotifyProxies.ts @@ -1,5 +1,4 @@ -/* eslint-disable no-console */ -import Cookies from 'universal-cookie/es6' +import Cookies from 'universal-cookie' import axios from 'axios' import isElectron from 'is-electron' import qs from 'qs' @@ -387,7 +386,8 @@ export async function spotifyPlaySong( return 'Success' } return 'Error' - } catch (_error) { + } catch (error) { + console.log(error) const { showSnackbar } = useStore.getState().ui showSnackbar('error', 'Song is not available') return 'Error'