diff --git a/package-lock.json b/package-lock.json index 3eea841..ac416f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,12 +19,15 @@ "axios": "1.6.7", "crypto-hash": "3.0.0", "crypto-js": "^4.2.0", + "dnd-core": "16.0.1", "file-saver": "2.0.5", "jszip": "3.10.1", "lodash": "4.17.21", "mui-file-input": "4.0.4", "nostr-tools": "2.3.1", "react": "^18.2.0", + "react-dnd": "16.0.1", + "react-dnd-html5-backend": "16.0.1", "react-dom": "^18.2.0", "react-redux": "9.1.0", "react-router-dom": "6.22.1", @@ -1641,6 +1644,21 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, "node_modules/@reduxjs/toolkit": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.1.tgz", @@ -2020,7 +2038,7 @@ "version": "20.11.20", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", - "dev": true, + "devOptional": true, "peer": true, "dependencies": { "undici-types": "~5.26.4" @@ -2762,6 +2780,24 @@ "node": ">=8" } }, + "node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, + "node_modules/dnd-core/node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3212,8 +3248,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -4392,6 +4427,43 @@ "node": ">=0.10.0" } }, + "node_modules/react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dependencies": { + "dnd-core": "^16.0.1" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -4989,7 +5061,7 @@ "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, + "devOptional": true, "peer": true }, "node_modules/update-browserslist-db": { diff --git a/package.json b/package.json index fcc2c1c..da3e20a 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,15 @@ "axios": "1.6.7", "crypto-hash": "3.0.0", "crypto-js": "^4.2.0", + "dnd-core": "16.0.1", "file-saver": "2.0.5", "jszip": "3.10.1", "lodash": "4.17.21", "mui-file-input": "4.0.4", "nostr-tools": "2.3.1", "react": "^18.2.0", + "react-dnd": "16.0.1", + "react-dnd-html5-backend": "16.0.1", "react-dom": "^18.2.0", "react-redux": "9.1.0", "react-router-dom": "6.22.1", diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index 1ddd769..6b7a2a3 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -1,4 +1,4 @@ -import { Clear } from '@mui/icons-material' +import { Clear, DragHandle } from '@mui/icons-material' import { Box, Button, @@ -20,7 +20,7 @@ import { } from '@mui/material' import JSZip from 'jszip' import { MuiFileInput } from 'mui-file-input' -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { useSelector } from 'react-redux' import { useNavigate } from 'react-router-dom' import { toast } from 'react-toastify' @@ -43,6 +43,10 @@ import { uploadToFileStorage } from '../../utils' import styles from './style.module.scss' +import { DndProvider } from 'react-dnd' +import { HTML5Backend } from 'react-dnd-html5-backend' +import type { Identifier, XYCoord } from 'dnd-core' +import { useDrag, useDrop } from 'react-dnd' export const CreatePage = () => { const navigate = useNavigate() @@ -132,7 +136,7 @@ export const CreatePage = () => { setLoadingSpinnerDesc('') }) - if (nip05Profile) { + if (nip05Profile && nip05Profile.pubkey) { const pubkey = nip05Profile.pubkey addUser(pubkey) setUserInput('') @@ -145,25 +149,34 @@ export const CreatePage = () => { setError('Invalid input! Make sure to provide correct npub or nip05.') } - const handleUserRoleChange = (role: UserRole, index: number) => { - setUsers((prevUsers) => { - // Create a shallow copy of the previous state - const updatedUsers = [...prevUsers] - // Create a shallow copy of the user object at the specified index - const updatedUser = { ...updatedUsers[index] } - // Update the role property of the copied user object - updatedUser.role = role - // Update the user object at the specified index in the copied array - updatedUsers[index] = updatedUser - // Return the updated array - return updatedUsers - }) + const handleUserRoleChange = (role: UserRole, pubkey: string) => { + setUsers((prevUsers) => + prevUsers.map((user) => { + if (user.pubkey === pubkey) { + return { + ...user, + role + } + } + + return user + }) + ) } const handleRemoveUser = (pubkey: string) => { setUsers((prev) => prev.filter((user) => user.pubkey !== pubkey)) } + const moveSinger = (dragIndex: number, hoverIndex: number) => { + setUsers((prevUsers) => { + const updatedUsers = [...prevUsers] + const [draggedUser] = updatedUsers.splice(dragIndex, 1) + updatedUsers.splice(hoverIndex, 0, draggedUser) + return updatedUsers + }) + } + const handleSelectFiles = (files: File[]) => { setDisplayUserInput(true) setSelectedFiles((prev) => { @@ -435,6 +448,7 @@ export const CreatePage = () => { users={users} handleUserRoleChange={handleUserRoleChange} handleRemoveUser={handleRemoveUser} + moveSigner={moveSinger} />