diff --git a/package-lock.json b/package-lock.json
index 479088b..2bcd952 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -34,9 +34,10 @@
"nostr-tools": "2.7.0",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "^4.4.168",
+ "rdndmb-html5-to-touch": "^8.0.3",
"react": "^18.2.0",
- "react-dnd": "16.0.1",
- "react-dnd-html5-backend": "16.0.1",
+ "react-dnd": "^16.0.1",
+ "react-dnd-multi-backend": "^8.0.3",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-redux": "9.1.0",
@@ -3267,6 +3268,19 @@
"@babel/runtime": "^7.9.2"
}
},
+ "node_modules/dnd-multi-backend": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/dnd-multi-backend/-/dnd-multi-backend-8.0.3.tgz",
+ "integrity": "sha512-yFFARotr+OEJk787Fsj+V52pi6j7+Pt/CRp3IR2Ai3fnxA/z6J54T7+gxkXzXu4cvxTNE7NiBzzAaJ2f7JjFTw==",
+ "license": "MIT",
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/LouisBrunner"
+ },
+ "peerDependencies": {
+ "dnd-core": "^16.0.1"
+ }
+ },
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -5687,6 +5701,21 @@
}
]
},
+ "node_modules/rdndmb-html5-to-touch": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/rdndmb-html5-to-touch/-/rdndmb-html5-to-touch-8.0.3.tgz",
+ "integrity": "sha512-VfIbLjlL9NAnZzc2M5fGPCNkDyK12+ahgILGO5RjS7jkgUlxwB0c/XvxVQNfY/2ocg7isTY/G7tqxJk5fSTZAA==",
+ "license": "MIT",
+ "dependencies": {
+ "dnd-multi-backend": "^8.0.3",
+ "react-dnd-html5-backend": "^16.0.1",
+ "react-dnd-touch-backend": "^16.0.1"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/LouisBrunner"
+ }
+ },
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
@@ -5702,6 +5731,7 @@
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
"integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
+ "license": "MIT",
"dependencies": {
"@react-dnd/invariant": "^4.0.1",
"@react-dnd/shallowequal": "^4.0.1",
@@ -5731,10 +5761,55 @@
"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==",
+ "license": "MIT",
"dependencies": {
"dnd-core": "^16.0.1"
}
},
+ "node_modules/react-dnd-multi-backend": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/react-dnd-multi-backend/-/react-dnd-multi-backend-8.0.3.tgz",
+ "integrity": "sha512-IwH7Mf6R05KIFohX0hHMTluoAvuUD8SO15KCD+9fY0nJ4nc1FGCMCSyMZw8R1XNStKp+JnNg3ZMtiaf5DebSUg==",
+ "license": "MIT",
+ "dependencies": {
+ "dnd-multi-backend": "^8.0.3",
+ "react-dnd-preview": "^8.0.3"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/LouisBrunner"
+ },
+ "peerDependencies": {
+ "dnd-core": "^16.0.1",
+ "react": "^16.14.0 || ^17.0.2 || ^18.0.0",
+ "react-dnd": "^16.0.1",
+ "react-dom": "^16.14.0 || ^17.0.2 || ^18.0.0"
+ }
+ },
+ "node_modules/react-dnd-preview": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/react-dnd-preview/-/react-dnd-preview-8.0.3.tgz",
+ "integrity": "sha512-s69Ro47QYDthDhj73iQ0VioMCjtlZ1AytKBDkQaHKm5DTjA8D2bIaFKCBQd330QEW0SIzqLJrZGCSlIY2xraJg==",
+ "license": "MIT",
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/LouisBrunner"
+ },
+ "peerDependencies": {
+ "react": "^16.14.0 || ^17.0.2 || ^18.0.0",
+ "react-dnd": "^16.0.1"
+ }
+ },
+ "node_modules/react-dnd-touch-backend": {
+ "version": "16.0.1",
+ "resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-16.0.1.tgz",
+ "integrity": "sha512-NonoCABzzjyWGZuDxSG77dbgMZ2Wad7eQiCd/ECtsR2/NBLTjGksPUx9UPezZ1nQ/L7iD130Tz3RUshL/ClKLA==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-dnd/invariant": "^4.0.1",
+ "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",
diff --git a/package.json b/package.json
index a2f074d..cf7ae91 100644
--- a/package.json
+++ b/package.json
@@ -44,9 +44,10 @@
"nostr-tools": "2.7.0",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "^4.4.168",
+ "rdndmb-html5-to-touch": "^8.0.3",
"react": "^18.2.0",
- "react-dnd": "16.0.1",
- "react-dnd-html5-backend": "16.0.1",
+ "react-dnd": "^16.0.1",
+ "react-dnd-multi-backend": "^8.0.3",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-redux": "9.1.0",
diff --git a/src/App.scss b/src/App.scss
index d3bff8a..4d95be6 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -141,6 +141,8 @@ li {
color: black;
letter-spacing: normal;
border: 1px solid transparent;
+
+ scroll-margin-top: $header-height + $body-vertical-padding;
}
[data-dev='true'] {
diff --git a/src/components/PDFView/PdfMarkItem.tsx b/src/components/PDFView/PdfMarkItem.tsx
index d5a7c78..db57800 100644
--- a/src/components/PDFView/PdfMarkItem.tsx
+++ b/src/components/PDFView/PdfMarkItem.tsx
@@ -2,6 +2,8 @@ import { CurrentUserMark } from '../../types/mark.ts'
import styles from '../DrawPDFFields/style.module.scss'
import { FONT_SIZE, FONT_TYPE, inPx } from '../../utils/pdf.ts'
import { useScale } from '../../hooks/useScale.tsx'
+import { forwardRef } from 'react'
+import { npubToHex } from '../../utils/nostr.ts'
interface PdfMarkItemProps {
userMark: CurrentUserMark
@@ -14,35 +16,41 @@ interface PdfMarkItemProps {
/**
* Responsible for display an individual Pdf Mark.
*/
-const PdfMarkItem = ({
- selectedMark,
- handleMarkClick,
- selectedMarkValue,
- userMark,
- pageWidth
-}: PdfMarkItemProps) => {
- const { location } = userMark.mark
- const handleClick = () => handleMarkClick(userMark.mark.id)
- const isEdited = () => selectedMark?.mark.id === userMark.mark.id
- const getMarkValue = () =>
- isEdited() ? selectedMarkValue : userMark.currentValue
- const { from } = useScale()
- return (
-
- {getMarkValue()}
-
- )
-}
+const PdfMarkItem = forwardRef(
+ (
+ { selectedMark, handleMarkClick, selectedMarkValue, userMark, pageWidth },
+ ref
+ ) => {
+ const { location } = userMark.mark
+ const handleClick = () => handleMarkClick(userMark.mark.id)
+ const isEdited = () => selectedMark?.mark.id === userMark.mark.id
+ const getMarkValue = () =>
+ isEdited() ? selectedMarkValue : userMark.currentValue
+ const { from } = useScale()
+ return (
+
+ {getMarkValue()}
+
+ )
+ }
+)
export default PdfMarkItem
diff --git a/src/components/PDFView/PdfPageItem.tsx b/src/components/PDFView/PdfPageItem.tsx
index ec9034a..bbf6e4e 100644
--- a/src/components/PDFView/PdfPageItem.tsx
+++ b/src/components/PDFView/PdfPageItem.tsx
@@ -48,16 +48,15 @@ const PdfPageItem = ({
alt={`page ${pageIndex + 1} of ${fileName}`}
/>
{currentUserMarks.map((m, i) => (
- (markRefs.current[m.id] = el)}>
-
-
+ (markRefs.current[m.id] = el)}
+ handleMarkClick={handleMarkClick}
+ selectedMarkValue={selectedMarkValue}
+ userMark={m}
+ selectedMark={selectedMark}
+ pageWidth={page.width}
+ />
))}
{otherUserMarks.map((m, i) => {
return (
diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx
index f1cfbd0..f0bd6b9 100644
--- a/src/pages/create/index.tsx
+++ b/src/pages/create/index.tsx
@@ -1,20 +1,13 @@
-import {
- Button,
- FormHelperText,
- ListItemIcon,
- ListItemText,
- MenuItem,
- Select,
- TextField,
- Tooltip
-} from '@mui/material'
+import styles from './style.module.scss'
+import { Button, FormHelperText, TextField, Tooltip } from '@mui/material'
import type { Identifier, XYCoord } from 'dnd-core'
import saveAs from 'file-saver'
import JSZip from 'jszip'
import { Event, kinds } from 'nostr-tools'
import { useEffect, useRef, useState } from 'react'
import { DndProvider, useDrag, useDrop } from 'react-dnd'
-import { HTML5Backend } from 'react-dnd-html5-backend'
+import { MultiBackend } from 'react-dnd-multi-backend'
+import { HTML5toTouch } from 'rdndmb-html5-to-touch'
import { useSelector } from 'react-redux'
import { useLocation, useNavigate } from 'react-router-dom'
import { toast } from 'react-toastify'
@@ -49,7 +42,6 @@ import {
uploadToFileStorage
} from '../../utils'
import { Container } from '../../components/Container'
-import styles from './style.module.scss'
import fileListStyles from '../../components/FileList/style.module.scss'
import { DrawTool, MarkType } from '../../types/drawing'
import { DrawPDFFields } from '../../components/DrawPDFFields'
@@ -874,21 +866,12 @@ export const CreatePage = () => {
setTitle(e.target.value)}
- sx={{
- width: '100%',
- fontSize: '16px',
- '& .MuiInputBase-input': {
- padding: '7px 14px'
- },
- '& .MuiOutlinedInput-notchedOutline': {
- display: 'none'
- }
- }}
/>
@@ -907,9 +890,6 @@ export const CreatePage = () => {
aria-label={`delete ${file.name}`}
variant="text"
onClick={(event) => handleRemoveFile(event, file)}
- sx={{
- minWidth: '44px'
- }}
>
@@ -932,76 +912,6 @@ export const CreatePage = () => {
}
right={
-
- setUserInput(e.target.value)}
- onKeyDown={handleInputKeyDown}
- error={!!error}
- fullWidth
- sx={{
- fontSize: '16px',
- '& .MuiInputBase-input': {
- padding: '7px 14px'
- },
- '& .MuiOutlinedInput-notchedOutline': {
- display: 'none'
- }
- }}
- />
-
-
-
-
{
moveSigner={moveSigner}
/>
-
+
+
+ setUserInput(e.target.value)}
+ onKeyDown={handleInputKeyDown}
+ error={!!error}
+ />
+
+
+
+
@@ -1032,11 +978,7 @@ export const CreatePage = () => {
{drawTool.active ? (
) : (
-
+
Coming soon
)}
@@ -1084,12 +1026,12 @@ const DisplayUser = ({
}: DisplayUsersProps) => {
return (
<>
-
+
{users
.filter((user) => user.role === UserRole.signer)
.map((user, index) => (
-
{users
.filter((user) => user.role === UserRole.viewer)
- .map((user, index) => {
- const userMeta = metadata[user.pubkey]
+ .map((user) => {
return (
-
-
-
-
-
-
-
-
+
+
)
})}
@@ -1179,23 +1065,26 @@ interface DragItem {
type: string
}
-type SignerRowProps = {
+type CounterpartProps = {
userMeta: ProfileMetadata
user: User
- index: number
- moveSigner: (dragIndex: number, hoverIndex: number) => void
handleUserRoleChange: (role: UserRole, pubkey: string) => void
handleRemoveUser: (pubkey: string) => void
}
-const SignerRow = ({
+type SignerCounterpartProps = CounterpartProps & {
+ index: number
+ moveSigner: (dragIndex: number, hoverIndex: number) => void
+}
+
+const SignerCounterpart = ({
userMeta,
user,
index,
moveSigner,
handleUserRoleChange,
handleRemoveUser
-}: SignerRowProps) => {
+}: SignerCounterpartProps) => {
const ref = useRef
(null)
const [{ handlerId }, drop] = useDrop<
@@ -1269,7 +1158,7 @@ const SignerRow = ({
})
})
- const opacity = isDragging ? 0 : 1
+ const opacity = isDragging ? 0.2 : 1
drag(drop(ref))
return (
@@ -1280,6 +1169,24 @@ const SignerRow = ({
ref={ref}
>
+
+
+ )
+}
+
+const Counterpart = ({
+ userMeta,
+ user,
+ handleUserRoleChange,
+ handleRemoveUser
+}: CounterpartProps) => {
+ return (
+ <>
-
-
+
-
+
+
+
+ >
)
}
diff --git a/src/pages/create/style.module.scss b/src/pages/create/style.module.scss
index eddacfe..a7dd7f2 100644
--- a/src/pages/create/style.module.scss
+++ b/src/pages/create/style.module.scss
@@ -42,6 +42,7 @@
}
button {
+ min-width: 44px;
color: $primary-main;
}
@@ -78,7 +79,7 @@
align-items: center;
flex-shrink: 0;
- height: 34px;
+ height: 36px;
overflow: hidden;
border-radius: 4px;
outline: solid 1px #dddddd;
@@ -89,11 +90,43 @@
&:focus-within {
outline-color: $primary-main;
}
+
+ // Override default MUI input styles only inside inputWrapepr
+ :global {
+ .MuiInputBase-input {
+ padding: 7px 14px;
+ }
+ .MuiOutlinedInput-notchedOutline {
+ display: none;
+ }
+ }
+}
+
+.addCounterpart {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: start;
+ gap: 10px;
+
+ > .inputWrapper {
+ flex-shrink: 1;
+ }
+
+ button {
+ min-width: 44px;
+ padding: 11px 12px;
+ }
}
.users {
flex-shrink: 0;
max-height: 33vh;
+
+ .counterpartToggleButton {
+ min-width: 44px;
+ padding: 11px 12px;
+ }
}
.user {
@@ -108,6 +141,22 @@
a:hover {
text-decoration: none;
}
+
+ // Higher specificify to override default button styles
+ .counterpartRowToggleButton {
+ min-width: 34px;
+ height: 34px;
+ padding: 0;
+ }
+}
+
+.counterpartRowToggleButton {
+ &[data-variant='primary'] {
+ color: $primary-main;
+ }
+ &[data-variant='secondary'] {
+ color: rgba(0, 0, 0, 0.35);
+ }
}
.avatar {
@@ -187,3 +236,7 @@
cursor: not-allowed;
}
}
+
+.comingSoonPlaceholder {
+ font-size: 10px;
+}
diff --git a/src/utils/string.ts b/src/utils/string.ts
index 9ac0f05..14eee83 100644
--- a/src/utils/string.ts
+++ b/src/utils/string.ts
@@ -1,5 +1,5 @@
/**
- * Function will replace the middle of the string with 3 dots if length greater then
+ * Function will replace the middle of the string with ellipsis if length greater then
* offset value
* @param str string to shorten
* @param offset of how many chars to keep in the beginning and the end
@@ -9,10 +9,7 @@ export const shorten = (str: string, offset = 9) => {
// return original string if it is not long enough
if (str.length < offset * 2 + 4) return str
- return `${str.slice(0, offset)}...${str.slice(
- str.length - offset,
- str.length
- )}`
+ return `${str.slice(0, offset)}…${str.slice(str.length - offset, str.length)}`
}
export const stringToHex = (str: string) => {