Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quote comment #4452

Merged
merged 8 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 61 additions & 2 deletions src/common/utils/dom.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

import { dom } from './dom'
import { dom, isElementInViewport } from './dom'

describe('utils/dom/getAttributes', () => {
it('should return an empty array if no matches are found', () => {
Expand All @@ -27,3 +27,62 @@ describe('utils/dom/getAttributes', () => {
expect(result).toEqual(['image.jpg'])
})
})

describe('utils/detect/isElementInViewport', () => {
let element: HTMLElement

beforeEach(() => {
element = document.createElement('div')
document.body.appendChild(element)

element.getBoundingClientRect = vi.fn(() => ({
top: 0,
left: 0,
bottom: 0,
right: 0,
width: 0,
height: 0,
x: 0,
y: 0,
toJSON: () => ({}),
}))
})

afterEach(() => {
document.body.removeChild(element)
})

it('should return true if the element is in the viewport', () => {
element.getBoundingClientRect = vi.fn(() => ({
top: 100,
left: 100,
bottom: 200,
right: 200,
width: 100,
height: 100,
x: 100,
y: 100,
toJSON: () => ({}),
}))

const result = isElementInViewport(element)
expect(result).toBe(true)
})

it('should return false if the element is not in the viewport', () => {
element.getBoundingClientRect = vi.fn(() => ({
top: 900,
left: 700,
bottom: 1000,
right: 800,
width: 100,
height: 100,
x: 700,
y: 900,
toJSON: () => ({}),
}))

const result = isElementInViewport(element)
expect(result).toBe(false)
})
})
11 changes: 11 additions & 0 deletions src/common/utils/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ export const dom = {
$$,
getAttributes,
}

export const isElementInViewport = (element: Element): boolean => {
const rect = element.getBoundingClientRect()
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <=
(window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
)
}
19 changes: 17 additions & 2 deletions src/components/CommentBeta/FooterActions/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Editor } from '@matters/matters-editor'
import gql from 'graphql-tag'
import { useContext, useEffect, useRef, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
Expand All @@ -16,6 +17,7 @@ import {
Media,
Spacer,
toast,
useCommentEditorContext,
ViewerContext,
} from '~/components'
import {
Expand Down Expand Up @@ -97,6 +99,8 @@ const BaseFooterActions = ({

const [showForm, setShowForm] = useState(false)
const toggleShowForm = () => setShowForm(!showForm)
const [editor, setEditor] = useState<Editor | null>(null)
const { setActiveEditor, activeEditor } = useCommentEditorContext()

const { state, node } = comment
const article = node.__typename === 'Article' ? node : undefined
Expand Down Expand Up @@ -224,7 +228,12 @@ const BaseFooterActions = ({
{...buttonProps}
{...replyButtonProps}
{...replyCustomButtonProps}
onClick={toggleShowForm}
onClick={() => {
if (editor === activeEditor) {
setActiveEditor(null)
}
toggleShowForm()
}}
/>
</Media>
</>
Expand All @@ -237,11 +246,17 @@ const BaseFooterActions = ({
<Spacer size="base" />
<CommentFormBeta
articleId={article?.id}
setEditor={setEditor}
type={'article'}
replyToId={comment.id}
parentId={comment.parentComment?.id || comment.id}
submitCallback={submitCallback}
closeCallback={() => setShowForm(false)}
closeCallback={() => {
if (editor === activeEditor) {
setActiveEditor(null)
}
setShowForm(false)
}}
isInCommentDetail={isInCommentDetail}
defaultContent={defaultContent}
/>
Expand Down
23 changes: 0 additions & 23 deletions src/components/Context/ActiveCommentEditor/index.tsx

This file was deleted.

44 changes: 44 additions & 0 deletions src/components/Context/CommentEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Editor } from '@matters/matters-editor'
import { createContext, ReactNode, useContext, useState } from 'react'

interface CommentEditorContextProps {
activeEditor: Editor | null
fallbackEditor: Editor | null
setActiveEditor: (editor: Editor | null) => void
setFallbackEditor: (editor: Editor | null) => void
getCurrentEditor: () => Editor | null
}

const CommentEditorContext = createContext<CommentEditorContextProps>(
{} as CommentEditorContextProps
)

export const useCommentEditorContext = (): CommentEditorContextProps => {
const context = useContext(CommentEditorContext)
return context
}

export const CommentEditorProvider = ({
children,
}: {
children: ReactNode
}) => {
const [activeEditor, setActiveEditor] = useState<Editor | null>(null)
const [fallbackEditor, setFallbackEditor] = useState<Editor | null>(null)

const getCurrentEditor = () => activeEditor || fallbackEditor

return (
<CommentEditorContext.Provider
value={{
activeEditor,
setActiveEditor,
fallbackEditor,
setFallbackEditor,
getCurrentEditor,
}}
>
{children}
</CommentEditorContext.Provider>
)
}
2 changes: 1 addition & 1 deletion src/components/Context/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from './ActiveCommentEditor'
export * from './CommentDrafts'
export * from './CommentEditor'
export * from './DraftDetailState'
export * from './Features'
export * from './Language'
Expand Down
21 changes: 13 additions & 8 deletions src/components/Editor/Comment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import {
EditorContent,
useCommentEditor,
} from '@matters/matters-editor'
import { useContext, useEffect } from 'react'
import { useEffect } from 'react'
import { useIntl } from 'react-intl'

import { BYPASS_SCROLL_LOCK, ENBABLE_SCROLL_LOCK } from '~/common/enums'
import { ActiveCommentEditorContext } from '~/components/Context'
import { useCommentEditorContext } from '~/components/Context'

import { makeMentionSuggestion } from '../Article/extensions'
import styles from './styles.module.css'
Expand All @@ -18,19 +18,19 @@ interface Props {
update: (params: { content: string }) => void
placeholder?: string
setEditor?: (editor: Editor | null) => void
syncQuote?: boolean
isFallbackEditor?: boolean
}

const CommentEditor: React.FC<Props> = ({
content,
update,
placeholder,
setEditor,
syncQuote,
isFallbackEditor,
}) => {
const client = useApolloClient()
const intl = useIntl()
const { setEditor: setActiveEditor } = useContext(ActiveCommentEditorContext)
const { setActiveEditor, setFallbackEditor } = useCommentEditorContext()

const editor = useCommentEditor({
placeholder:
Expand Down Expand Up @@ -58,8 +58,8 @@ const CommentEditor: React.FC<Props> = ({

useEffect(() => {
setEditor?.(editor)
if (syncQuote) {
setActiveEditor(editor)
if (isFallbackEditor) {
setFallbackEditor(editor)
}
}, [editor])

Expand All @@ -68,7 +68,12 @@ const CommentEditor: React.FC<Props> = ({
className={styles.commentEditor}
id="editor" // anchor for mention plugin
>
<EditorContent editor={editor} />
<EditorContent
editor={editor}
onFocus={() => {
setActiveEditor(editor)
}}
/>
</div>
)
}
Expand Down
21 changes: 16 additions & 5 deletions src/components/Forms/CommentFormBeta/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
CommentDraftsContext,
SpinnerBlock,
TextIcon,
useCommentEditorContext,
useMutation,
useRoute,
ViewerContext,
Expand All @@ -31,11 +32,12 @@ import styles from './styles.module.css'
export type CommentFormBetaType = 'article'

export interface CommentFormBetaProps {
type: CommentFormBetaType

commentId?: string
replyToId?: string
parentId?: string
articleId?: string
type: CommentFormBetaType
isInCommentDetail?: boolean

defaultContent?: string | null
Expand All @@ -44,7 +46,9 @@ export interface CommentFormBetaProps {

showClear?: boolean
placeholder?: string
syncQuote?: boolean

isFallbackEditor?: boolean
setEditor?: (editor: Editor | null) => void
}

export const CommentFormBeta: React.FC<CommentFormBetaProps> = ({
Expand All @@ -59,15 +63,21 @@ export const CommentFormBeta: React.FC<CommentFormBetaProps> = ({
closeCallback,
showClear,
placeholder,
syncQuote,
isFallbackEditor,
setEditor: propsSetEditor,
}) => {
const intl = useIntl()
const viewer = useContext(ViewerContext)
const { getDraft, updateDraft, removeDraft } =
useContext(CommentDraftsContext)
const { getQuery, routerLang } = useRoute()
const { setActiveEditor } = useCommentEditorContext()
const [editor, localSetEditor] = useState<Editor | null>(null)
const setEditor = (editor: Editor | null) => {
localSetEditor(editor)
propsSetEditor?.(editor)
}
const shortHash = getQuery('shortHash')
const [editor, setEditor] = useState<Editor | null>(null)

// retrieve comment draft
const commentDraftId = `${articleId}-${type}-${commentId || 0}-${
Expand Down Expand Up @@ -172,6 +182,7 @@ export const CommentFormBeta: React.FC<CommentFormBetaProps> = ({
editor.commands.setContent('')
}
removeDraft(commentDraftId)
setActiveEditor(null)
}

const onUpdate = ({ content: newContent }: { content: string }) => {
Expand All @@ -195,7 +206,7 @@ export const CommentFormBeta: React.FC<CommentFormBetaProps> = ({
content={content}
update={onUpdate}
placeholder={placeholder}
syncQuote={syncQuote}
isFallbackEditor={isFallbackEditor}
setEditor={(editor) => {
setEditor(editor)
}}
Expand Down
Loading
Loading