M src/components/MainPoint.tsx => src/components/MainPoint.tsx +33 -11
@@ 23,8 23,9 @@ import { ReplyFill, ArrowUp, XLg, ArrowsMove, Files } from 'react-bootstrap-icon
import './point.scss'
import './button.scss'
-import { getPointIfReference, getReferenceData, isDraft } from '../dataModels/pointUtils'
+import { getPointIfReference, getReferenceData, isDraft, isQuote } from '../dataModels/pointUtils'
import { useDragPoint } from '../hooks/useDragPoint'
+import { ReadOnlyPoint } from './ReadOnlyPoint'
import Point from './Point'
import { useAppSelector, useAppDispatch } from '../hooks/useRedux'
@@ 66,7 67,7 @@ export const MainPoint = ({ messageURL, pointURL }: Props): ReactElement => {
}
const handleShapeIconClick = (e: React.MouseEvent): void => {
- setShowOptions(true)
+ /* setShowOptions(true) */
dispatch(togglePoint({ pointURL }))
}
@@ 160,26 161,47 @@ export const MainPoint = ({ messageURL, pointURL }: Props): ReactElement => {
}
}
+ const readOnlyPointButtons = (
+ <>
+ {(isQuote(point)) && (
+ <button className='button' onClick={handleViewOriginalMessageButtonClick} title='View original message'>
+ <ArrowUp className='bi' />
+ </button>
+ )}
+ <button className='button' onClick={handleReplyButtonClick} title='Reply to this point'>
+ <ReplyFill className='bi' />
+ </button>
+ <button className='button' onClick={handleMoveOrQuotePointsClick} title='Quote points'>
+ <Files className='bi' />
+ </button>
+ </>
+ )
+
let pointWrapperClassName = 'main-point'
if (isSelected) pointWrapperClassName += ' selected'
+ if (!isDraft(pointURL) || (isQuote(point))) {
+ return (
+ <ReadOnlyPoint
+ pointURL={pointURL}
+ isSelected={isSelected}
+ buttons={readOnlyPointButtons}
+ />
+ )
+ }
+
return (
<div className={pointWrapperClassName} onClick={handlePointClick}>
<Point
- url={pointURL}
- displayPoint={{ ...point, content }}
- referenceData={referenceData}
- buttons={buttons}
+ pointURL={pointURL}
+ messageURL={messageURL}
isMainPoint
isSelected={isSelected}
- showOptions={showOptions}
- readOnlyOverride={!isDraft(pointURL)}
- handleChange={handleChange}
- handleKeyDown={handleKeyDown}
- handleBlur={handleBlur}
+ expandedRegion={expandedRegion}
handleShapeIconClick={handleShapeIconClick}
ref={pointRef}
/>
</div>
)
}
+
M src/components/Point.tsx => src/components/Point.tsx +31 -14
@@ 31,6 31,20 @@ import './point.scss'
import './button.scss'
import { useAppDispatch, useAppSelector } from '../hooks/useRedux'
+import { jumpToPrevPointThunk, jumpToNextPointThunk, clearCursorPosition } from '../slices/cursorPosition'
+import {
+ draftPointCreate,
+ splitIntoTwoPoints,
+ combinePointsThunk,
+ pointsMoveWithinMessageThunk,
+ draftPointUpdate,
+ deletePointsThunk,
+ autoDeleteEmptyPoint,
+ setMainThunk
+} from '../slices/drafts'
+import { hoverOver } from '../slices/drag'
+import { togglePoint, viewOriginalMessage } from '../slices/selectedPoints'
+import { openMovePointsDraftsModal, openReplyToPointDraftsModal } from '../slices/modal'
import { Banner } from './Banner'
import { AllShapes } from './AllShapes'
@@ 38,6 52,7 @@ import { useTextareaIndent } from '../hooks/useTextareaIndent'
interface Props {
pointURL: string
+ messageURL: string
// TODO: make not optional
isMainPoint: boolean
isSelected: boolean
@@ 46,10 61,11 @@ interface Props {
}
// TODO: fix ref type below
-const Point = forwardRef<any, Props>((props, ref) => {
+const Point = forwardRef<any>((props, ref) => {
const dispatch = useAppDispatch()
+ const { pointURL, messageURL, isMainPoint, isSelected, expandedRegion, handleShapeIconClick } = props
- const point = useAppSelector(({ drafts }) => drafts.byURL[messageURL].points[pointURL])
+ const point = useAppSelector(({ drafts }) => drafts.byURL[messageURL].present.points[pointURL])
const divRef = useRef<HTMLDivElement>(null)
const buttonRef = useRef<HTMLButtonElement>(null)
@@ 101,8 117,6 @@ const Point = forwardRef<any, Props>((props, ref) => {
}
const handleKeyDown = (e: React.KeyboardEvent): void => {
- if (!isDraft(pointURL) || referenceData !== undefined) return
-
const textareaRef = pointRef.current.textarea
const isAtBeginning = textareaRef.selectionStart === 0
const isAtEnd = textareaRef.selectionEnd === textareaRef.value.length
@@ 146,7 160,6 @@ const Point = forwardRef<any, Props>((props, ref) => {
if (!e.currentTarget.contains(e.relatedTarget as Node)) { // Don't hide options if focus switches to another child of point-wrapper div
setShowOptions(false)
}
- if (!isDraft(pointURL) || referenceData !== undefined) return
if (content === '') {
dispatch(autoDeleteEmptyPoint({ messageURL, pointURLs: [pointURL] }))
} else {
@@ 162,24 175,28 @@ const Point = forwardRef<any, Props>((props, ref) => {
// The useState and useEffect are purely to cause the component to
// re-render after it first mounts. A better solution must exist.
const [, setCounter] = useState(0)
- const { expandedRegion } = props
useEffect(() => {
setCounter((c) => c + 1)
}, [expandedRegion, showOptions])
- const handleShapeIconClick = (e: React.MouseEvent): void => {
+ const handleShapeButtonClick = (e: React.MouseEvent): void => {
setShowOptions(true)
- props.handleShapeIconClick(e)
+ handleShapeIconClick(e)
+ }
+
+ function handleMoveOrQuotePointsClick (e: React.MouseEvent): void {
+ dispatch(openMovePointsDraftsModal({ pointURL }))
+ e.stopPropagation()
+ }
+
+ function handleXButtonClick (e: React.MouseEvent): void {
+ dispatch(deletePointsThunk({ pointURLs: [pointURL], messageURL }))
+ e.stopPropagation()
}
// TODO: Different buttons if main point
const buttons = (
<>
- {(referenceData !== undefined) && (
- <button className='button' onClick={handleViewOriginalMessageButtonClick} title='View original message'>
- <ArrowUp className='bi' />
- </button>
- )}
<button className='button' onClick={handleSetMainPointButtonClick} title='Set main point'>
<ExclamationLg className='bi' />
</button>
@@ 204,7 221,7 @@ const Point = forwardRef<any, Props>((props, ref) => {
<div className='button-group' ref={buttonGroupRef}>
<button
className='button'
- onClick={handleShapeIconClick}
+ onClick={handleShapeButtonClick}
title='Select point'
ref={buttonRef}
>
M src/components/ReadOnlyPoint.tsx => src/components/ReadOnlyPoint.tsx +14 -5
@@ 38,7 38,7 @@ interface Props {
buttons?: React.ReactNode
}
-export const ReadOnlyPoint = ({ pointURL, isSelected, buttons }: Props): ReactElement => {
+export const ReadOnlyPoint = ({ pointURL, isSelected, handleShapeIconClick, buttons }: Props): ReactElement => {
const point = useAppSelector(({ published, drafts }) => getPointByURL(pointURL, published, drafts))
const { content, shape, createdAt } = useAppSelector(({ published, drafts }) => getPointIfReference(pointURL, published, drafts))
const { authorURL, borderColor } = useAppSelector(({ published, drafts, authors }) => {
@@ 55,7 55,7 @@ export const ReadOnlyPoint = ({ pointURL, isSelected, buttons }: Props): ReactEl
const [showOptions, setShowOptions] = useState(false)
- const handleShapeIconClick = (e: React.MouseEvent): void => {
+ const handleShapeButtonClick = (e: React.MouseEvent): void => {
setShowOptions(true)
if (handleShapeIconClick !== undefined) handleShapeIconClick(e)
}
@@ 63,13 63,21 @@ export const ReadOnlyPoint = ({ pointURL, isSelected, buttons }: Props): ReactEl
function handlePointClick (e: React.MouseEvent): void {
if (isExpanded) e.stopPropagation()
}
-
+
+ function handleBlur (e: React.FocusEvent<HTMLDivElement>): void {
+ if (!e.currentTarget.contains(e.relatedTarget as Node)) { // Don't hide options if focus switches to another child of point-wrapper div
+ setShowOptions(false)
+ }
+ }
+
let readOnlyPointWrapperClassName = 'read-only-point-wrapper'
if (isQuote(point)) readOnlyPointWrapperClassName += ' quote'
if (isSelected) readOnlyPointWrapperClassName += ' selected'
return (
- <div onClick={handlePointClick}>
+ <div
+ onClick={handlePointClick}
+ >
{isQuote(point) && (
<div className='read-only-point-top-bar'>
<TimeStamp createdAt={createdAt} />
@@ 78,12 86,13 @@ export const ReadOnlyPoint = ({ pointURL, isSelected, buttons }: Props): ReactEl
)}
<div
className={readOnlyPointWrapperClassName}
+ onBlur={handleBlur}
style={{ borderColor }}
>
<div className='button-group'>
<button
className='button'
- onClick={handleShapeIconClick}
+ onClick={handleShapeButtonClick}
title='Select point'
>
<AllShapes shape={shape} />
M src/components/RegionPoint.tsx => src/components/RegionPoint.tsx +3 -2
@@ 155,7 155,7 @@ export const RegionPoint = ({ messageURL, pointURL, index }: Props): ReactElemen
e.stopPropagation()
}
- const buttonsIfReadOnlyPoint = (
+ const readOnlyPointButtons = (
<>
{(isQuote(point)) && (
<button className='button' onClick={handleViewOriginalMessageButtonClick} title='View original message'>
@@ 179,7 179,7 @@ export const RegionPoint = ({ messageURL, pointURL, index }: Props): ReactElemen
<ReadOnlyPoint
pointURL={pointURL}
isSelected={isSelected}
- buttons={buttonsIfReadOnlyPoint}
+ buttons={readOnlyPointButtons}
/>
)
}
@@ 188,6 188,7 @@ export const RegionPoint = ({ messageURL, pointURL, index }: Props): ReactElemen
<div className={pointWrapperClassName} onClick={handlePointClick}>
<Point
pointURL={pointURL}
+ messageURL={messageURL}
isMainPoint={false}
isSelected={isSelected}
expandedRegion={expandedRegion}