diff --git a/packages/web/components/patterns/HighlightBar.tsx b/packages/web/components/patterns/HighlightBar.tsx index ee1b16414..98a92a39a 100644 --- a/packages/web/components/patterns/HighlightBar.tsx +++ b/packages/web/components/patterns/HighlightBar.tsx @@ -6,7 +6,7 @@ import { StyledText } from '../elements/StyledText' import { Button } from '../elements/Button' import { HStack, Box } from '../elements/LayoutPrimitives' import { PenWithColorIcon } from '../elements/images/PenWithColorIcon' -import { Note, Tag, Trash } from 'phosphor-react' +import { Note, Tag, Trash, Copy } from 'phosphor-react' type PageCoordinates = { pageX: number @@ -21,6 +21,7 @@ export type HighlightAction = | 'post' | 'unshare' | 'setHighlightLabels' + | 'copy' type HighlightBarProps = { anchorCoordinates: PageCoordinates @@ -31,50 +32,75 @@ type HighlightBarProps = { } export function HighlightBar(props: HighlightBarProps): JSX.Element { - if (props.displayAtBottom) { - return ( - - - - ) - } else { - return ( - - - - ) - } + }), + ...(!props.displayAtBottom && { left: props.anchorCoordinates.pageX }), + ...(!props.displayAtBottom && { top: props.anchorCoordinates.pageY }), + }} + > + + + ) +} + +type BarButtonProps = { + title: string + onClick: VoidFunction + iconElement: JSX.Element + text: string +} + +function BarButton({ text, title, iconElement, onClick }: BarButtonProps) { + return ( + + ) } function BarContent(props: HighlightBarProps): JSX.Element { @@ -96,116 +122,51 @@ function BarContent(props: HighlightBarProps): JSX.Element { }} > {props.isNewHighlight ? ( - + /> ) : ( <> - + } + onClick={() => props.handleButtonClick('delete')} + /> - - + } + onClick={() => props.handleButtonClick('setHighlightLabels')} + /> )} - - {/* - */} + } + onClick={() => props.handleButtonClick('comment')} + /> + + + } + onClick={() => props.handleButtonClick('copy')} + /> ) } diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index a0f13faee..12f4a7fc1 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -10,6 +10,7 @@ import type { HighlightLocation } from '../../../lib/highlights/highlightGenerat import { useSelection } from '../../../lib/highlights/useSelection' import type { Highlight } from '../../../lib/networking/fragments/highlightFragment' import { + getHighlightElements, highlightIdAttribute, highlightNoteIdAttribute, SelectionAttributes, @@ -19,7 +20,7 @@ import { removeHighlights } from '../../../lib/highlights/deleteHighlight' import { createHighlight } from '../../../lib/highlights/createHighlight' import { HighlightNoteModal } from './HighlightNoteModal' import { NotebookModal } from './NotebookModal' -import { showErrorToast } from '../../../lib/toastHelpers' +import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { ArticleMutations } from '../../../lib/articleActions' import { isTouchScreenDevice } from '../../../lib/deviceType' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' @@ -263,7 +264,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { } const createHighlightCallback = useCallback( - async (successAction: HighlightModalAction, annotation?: string) => { + async (annotation?: string) => { if (!selectionData) { return } @@ -395,7 +396,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { ) const handleCloseNotebook = useCallback( - (updatedHighlights: Highlight[], deletedHighlights: Highlight[]) => { + (updatedHighlights: Highlight[]) => { props.setShowHighlightsModal(false) // Remove all the existing highlights, then set the new ones @@ -454,6 +455,41 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { }) } break + case 'copy': { + const selection = window.getSelection() + if (selection === null) return + + const userSelectionText = selection.toString() + let textToCopy = '' + + if (focusedHighlight) { + const highlightedElements = getHighlightElements( + focusedHighlight.id + ) + highlightedElements.forEach( + (element) => (textToCopy += element.textContent) + ) + } else if (userSelectionText) { + textToCopy = userSelectionText + } + + if (textToCopy) { + try { + await navigator.clipboard.writeText(textToCopy) + showSuccessToast('Highlight copied', { + position: 'bottom-right', + }) + } catch (error) { + showErrorToast('Error copying highlight, permission denied.', { + position: 'bottom-right', + }) + } + } + + selection.empty() + setSelectionData(null) + break + } case 'setHighlightLabels': if (props.isAppleAppEmbed) { window?.webkit?.messageHandlers.highlightAction?.postMessage({ @@ -474,6 +510,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { props.isAppleAppEmbed, removeHighlightCallback, selectionData, + setSelectionData, ] ) @@ -615,7 +652,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { dispatchHighlightMessage('noteCreated') } else { try { - await createHighlightCallback('none', event.annotation) + await createHighlightCallback('none') dispatchHighlightMessage('noteCreated') } catch (error) { dispatchHighlightError('saveAnnotation', error)