diff --git a/package.json b/package.json index b9482526..ad6a855b 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,9 @@ ] }, "dependencies": { + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/utilities": "^3.2.2", "@formkit/auto-animate": "^0.8.1", "@headlessui/react": "^1.7.17", "@ladjs/country-language": "^1.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54dd87b7..0bca4afe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,15 @@ overrides: rollup: npm:@rollup/wasm-node dependencies: + '@dnd-kit/core': + specifier: ^6.1.0 + version: 6.1.0(react-dom@18.2.0)(react@18.2.0) + '@dnd-kit/sortable': + specifier: ^8.0.0 + version: 8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0) + '@dnd-kit/utilities': + specifier: ^3.2.2 + version: 3.2.2(react@18.2.0) '@formkit/auto-animate': specifier: ^0.8.1 version: 0.8.1 @@ -274,7 +283,7 @@ devDependencies: version: 0.5.9(prettier@3.1.1) rollup-plugin-visualizer: specifier: ^5.11.0 - version: 5.11.0(@rollup/wasm-node@4.14.2) + version: 5.11.0(@rollup/wasm-node@4.14.3) tailwind-scrollbar: specifier: ^3.0.5 version: 3.0.5(tailwindcss@3.4.0) @@ -2688,6 +2697,49 @@ packages: to-fast-properties: 2.0.0 dev: true + /@dnd-kit/accessibility@3.1.0(react@18.2.0): + resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /@dnd-kit/core@6.1.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@dnd-kit/accessibility': 3.1.0(react@18.2.0) + '@dnd-kit/utilities': 3.2.2(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tslib: 2.6.2 + dev: false + + /@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0): + resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==} + peerDependencies: + '@dnd-kit/core': ^6.1.0 + react: '>=16.8.0' + dependencies: + '@dnd-kit/core': 6.1.0(react-dom@18.2.0)(react@18.2.0) + '@dnd-kit/utilities': 3.2.2(react@18.2.0) + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /@dnd-kit/utilities@3.2.2(react@18.2.0): + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.2.0 + tslib: 2.6.2 + dev: false + /@esbuild/aix-ppc64@0.19.10: resolution: {integrity: sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q==} engines: {node: '>=12'} @@ -3175,7 +3227,7 @@ packages: engines: {node: '>=14.0.0'} dev: false - /@rollup/plugin-babel@5.3.1(@babel/core@7.24.3)(@rollup/wasm-node@4.14.2): + /@rollup/plugin-babel@5.3.1(@babel/core@7.24.3)(@rollup/wasm-node@4.14.3): resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} engines: {node: '>= 10.0.0'} peerDependencies: @@ -3188,36 +3240,36 @@ packages: dependencies: '@babel/core': 7.24.3 '@babel/helper-module-imports': 7.24.3 - '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.2) - rollup: /@rollup/wasm-node@4.14.2 + '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.3) + rollup: /@rollup/wasm-node@4.14.3 dev: true - /@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.14.2): + /@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.14.3): resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} engines: {node: '>= 10.0.0'} peerDependencies: rollup: npm:@rollup/wasm-node dependencies: - '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.2) + '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.3) '@types/resolve': 1.17.1 builtin-modules: 3.3.0 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.8 - rollup: /@rollup/wasm-node@4.14.2 + rollup: /@rollup/wasm-node@4.14.3 dev: true - /@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.14.2): + /@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.14.3): resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} peerDependencies: rollup: npm:@rollup/wasm-node dependencies: - '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.2) + '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.3) magic-string: 0.25.9 - rollup: /@rollup/wasm-node@4.14.2 + rollup: /@rollup/wasm-node@4.14.3 dev: true - /@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.14.2): + /@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.14.3): resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} engines: {node: '>= 8.0.0'} peerDependencies: @@ -3226,11 +3278,11 @@ packages: '@types/estree': 0.0.39 estree-walker: 1.0.1 picomatch: 2.3.1 - rollup: /@rollup/wasm-node@4.14.2 + rollup: /@rollup/wasm-node@4.14.3 dev: true - /@rollup/wasm-node@4.14.2: - resolution: {integrity: sha512-iwZbxtvP/0icwPWExUZWfA3A2jqQkDY38E8R5onRY2ALFmom0k7e37n9WDcJMMRcx/pdenfN8NaSohzX9LiDEQ==} + /@rollup/wasm-node@4.14.3: + resolution: {integrity: sha512-UyFUQV/iAu/Wt6rY6uQMYBQlfTMsynzYVIz6i7s9ySwjoG9WDNgtkK1TrazCSrUFbmuPZi2gbJm6VWdJCVw2yA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: @@ -6557,7 +6609,7 @@ packages: '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6) '@babel/types': 7.23.6 kleur: 4.1.5 - rollup: /@rollup/wasm-node@4.14.2 + rollup: /@rollup/wasm-node@4.14.3 unplugin: 1.5.1 transitivePeerDependencies: - supports-color @@ -7529,7 +7581,7 @@ packages: glob: 7.2.3 dev: true - /rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.14.2): + /rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.14.3): resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser peerDependencies: @@ -7537,12 +7589,12 @@ packages: dependencies: '@babel/code-frame': 7.24.2 jest-worker: 26.6.2 - rollup: /@rollup/wasm-node@4.14.2 + rollup: /@rollup/wasm-node@4.14.3 serialize-javascript: 4.0.0 terser: 5.30.0 dev: true - /rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.14.2): + /rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.14.3): resolution: {integrity: sha512-exM0Ms2SN3AgTzMeW7y46neZQcyLY7eKwWAop1ZoRTCZwyrIRdMMJ6JjToAJbML77X/9N8ZEpmXG4Z/Clb9k8g==} engines: {node: '>=14'} hasBin: true @@ -7554,7 +7606,7 @@ packages: dependencies: open: 8.4.2 picomatch: 2.3.1 - rollup: /@rollup/wasm-node@4.14.2 + rollup: /@rollup/wasm-node@4.14.3 source-map: 0.7.4 yargs: 17.7.2 dev: true @@ -8692,7 +8744,7 @@ packages: '@types/node': 20.10.5 esbuild: 0.19.10 postcss: 8.4.32 - rollup: /@rollup/wasm-node@4.14.2 + rollup: /@rollup/wasm-node@4.14.3 optionalDependencies: fsevents: 2.3.3 dev: true @@ -8959,9 +9011,9 @@ packages: '@babel/core': 7.24.3 '@babel/preset-env': 7.24.3(@babel/core@7.24.3) '@babel/runtime': 7.24.1 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.3)(@rollup/wasm-node@4.14.2) - '@rollup/plugin-node-resolve': 11.2.1(@rollup/wasm-node@4.14.2) - '@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.14.2) + '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.3)(@rollup/wasm-node@4.14.3) + '@rollup/plugin-node-resolve': 11.2.1(@rollup/wasm-node@4.14.3) + '@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.14.3) '@surma/rollup-plugin-off-main-thread': 2.2.3 ajv: 8.12.0 common-tags: 1.8.2 @@ -8970,8 +9022,8 @@ packages: glob: 7.2.3 lodash: 4.17.21 pretty-bytes: 5.6.0 - rollup: /@rollup/wasm-node@4.14.2 - rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.14.2) + rollup: /@rollup/wasm-node@4.14.3 + rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.14.3) source-map: 0.8.0-beta.0 stringify-object: 3.3.0 strip-comments: 2.0.1 @@ -9016,6 +9068,7 @@ packages: /workbox-google-analytics@7.0.0: resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained dependencies: workbox-background-sync: 7.0.0 workbox-core: 7.0.0 diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 6b951af6..acab2c56 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -527,6 +527,8 @@ "autoplay": "Autoplay", "autoplayDescription": "Automatically play the next episode in a series after reaching the end. Can be enabled by users with the browser extension, a custom proxy, or with the default setup if allowed by the host.", "autoplayLabel": "Autoplay", + "sourceOrder": "Reordering sources", + "sourceOrderDescription": "Drag and drop to reorder sources. This will determine the order in which sources are checked for the media you are trying to watch.", "title": "Preferences" }, "reset": "Reset", diff --git a/src/components/form/SortableList.tsx b/src/components/form/SortableList.tsx new file mode 100644 index 00000000..90c8fd94 --- /dev/null +++ b/src/components/form/SortableList.tsx @@ -0,0 +1,89 @@ +import { + DndContext, + DragEndEvent, + KeyboardSensor, + PointerSensor, + closestCenter, + useSensor, + useSensors, +} from "@dnd-kit/core"; +import { + SortableContext, + arrayMove, + sortableKeyboardCoordinates, + useSortable, + verticalListSortingStrategy, +} from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; +import classNames from "classnames"; + +import { Icon, Icons } from "../Icon"; + +function SortableItem(props: { id: string }) { + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ id: props.id }); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + }; + + return ( +
+ {t("settings.preferences.sourceOrder")} +
++ {t("settings.preferences.sourceOrderDescription")} +
+ +