Add emoji picker to chat composer (#5196)

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
Co-authored-by: Adrov Igor <nucleartux@gmail.com>
This commit is contained in:
Eric Bailey 2024-09-06 17:58:47 -05:00 committed by GitHub
parent 30d2ab8dd3
commit 543be17674
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 119 additions and 14 deletions

View file

@ -133,7 +133,7 @@ export const ComposePost = observer(function ComposePost({
quote: initQuote,
quoteCount,
mention: initMention,
openPicker,
openEmojiPicker,
text: initText,
imageUris: initImageUris,
cancelRef,
@ -520,8 +520,8 @@ export const ComposePost = observer(function ComposePost({
gallery.size > 0 || Boolean(extLink) || Boolean(videoUploadState.video)
const onEmojiButtonPress = useCallback(() => {
openPicker?.(textInput.current?.getCursorPosition())
}, [openPicker])
openEmojiPicker?.(textInput.current?.getCursorPosition())
}, [openEmojiPicker])
const focusTextInput = useCallback(() => {
textInput.current?.focus()

View file

@ -12,12 +12,12 @@ import {Placeholder} from '@tiptap/extension-placeholder'
import {Text as TiptapText} from '@tiptap/extension-text'
import {generateJSON} from '@tiptap/html'
import {EditorContent, JSONContent, useEditor} from '@tiptap/react'
import EventEmitter from 'eventemitter3'
import {usePalette} from '#/lib/hooks/usePalette'
import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {blobToDataUri, isUriImage} from 'lib/media/util'
import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter'
import {
LinkFacetMatch,
suggestLinkCardUri,
@ -46,8 +46,6 @@ interface TextInputProps {
onError: (err: string) => void
}
export const textInputWebEmitter = new EventEmitter()
export const TextInput = React.forwardRef(function TextInputImpl(
{
richtext,

View file

@ -0,0 +1,3 @@
import EventEmitter from 'eventemitter3'
export const textInputWebEmitter = new EventEmitter()

View file

@ -7,8 +7,8 @@ import {
} from 'react-native'
import Picker from '@emoji-mart/react'
import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter'
import {atoms as a} from '#/alf'
import {textInputWebEmitter} from '../TextInput.web'
const HEIGHT_OFFSET = 40
const WIDTH_OFFSET = 100
@ -26,22 +26,41 @@ export type Emoji = {
unified: string
}
export interface EmojiPickerPosition {
top: number
left: number
right: number
bottom: number
}
export interface EmojiPickerState {
isOpen: boolean
pos: {top: number; left: number; right: number; bottom: number}
pos: EmojiPickerPosition
}
interface IProps {
state: EmojiPickerState
close: () => void
/**
* If `true`, overrides position and ensures picker is pinned to the top of
* the target element.
*/
pinToTop?: boolean
}
export function EmojiPicker({state, close}: IProps) {
export function EmojiPicker({state, close, pinToTop}: IProps) {
const {height, width} = useWindowDimensions()
const isShiftDown = React.useRef(false)
const position = React.useMemo(() => {
if (pinToTop) {
return {
top: state.pos.top - PICKER_HEIGHT + HEIGHT_OFFSET - 10,
left: state.pos.left,
}
}
const fitsBelow = state.pos.top + PICKER_HEIGHT < height
const fitsAbove = PICKER_HEIGHT < state.pos.top
const placeOnLeft = PICKER_WIDTH < state.pos.left
@ -64,7 +83,7 @@ export function EmojiPicker({state, close}: IProps) {
: undefined,
}
}
}, [state.pos, height, width])
}, [state.pos, height, width, pinToTop])
React.useEffect(() => {
if (!state.isOpen) return

View file

@ -61,7 +61,7 @@ export function Composer({}: {winHeight: number}) {
quoteCount={state?.quoteCount}
onPost={state.onPost}
mention={state.mention}
openPicker={onOpenPicker}
openEmojiPicker={onOpenPicker}
text={state.text}
/>
</View>