mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Partially fixed #113
This commit is contained in:
161
src/components/Emojis.tsx
Normal file
161
src/components/Emojis.tsx
Normal file
@ -0,0 +1,161 @@
|
||||
import EmojisButton from '@components/Emojis/Button'
|
||||
import EmojisList from '@components/Emojis/List'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { useEmojisQuery } from '@utils/queryHooks/emojis'
|
||||
import { chunk, forEach, groupBy, sortBy } from 'lodash'
|
||||
import React, {
|
||||
createContext,
|
||||
Dispatch,
|
||||
MutableRefObject,
|
||||
SetStateAction,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useReducer
|
||||
} from 'react'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
|
||||
type EmojisState = {
|
||||
enabled: boolean
|
||||
active: boolean
|
||||
emojis: { title: string; data: Mastodon.Emoji[][] }[]
|
||||
shortcode: Mastodon.Emoji['shortcode'] | null
|
||||
}
|
||||
|
||||
type EmojisAction =
|
||||
| {
|
||||
type: 'load'
|
||||
payload: NonNullable<EmojisState['emojis']>
|
||||
}
|
||||
| {
|
||||
type: 'activate'
|
||||
payload: EmojisState['active']
|
||||
}
|
||||
| {
|
||||
type: 'shortcode'
|
||||
payload: EmojisState['shortcode']
|
||||
}
|
||||
|
||||
const emojisReducer = (state: EmojisState, action: EmojisAction) => {
|
||||
switch (action.type) {
|
||||
case 'activate':
|
||||
return { ...state, active: action.payload }
|
||||
case 'load':
|
||||
return { ...state, emojis: action.payload }
|
||||
case 'shortcode':
|
||||
return { ...state, shortcode: action.payload }
|
||||
}
|
||||
}
|
||||
|
||||
type ContextType = {
|
||||
emojisState: EmojisState
|
||||
emojisDispatch: Dispatch<EmojisAction>
|
||||
}
|
||||
const EmojisContext = createContext<ContextType>({} as ContextType)
|
||||
|
||||
const prefetchEmojis = (
|
||||
sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[],
|
||||
reduceMotionEnabled: boolean
|
||||
) => {
|
||||
const prefetches: { uri: string }[] = []
|
||||
let requestedIndex = 0
|
||||
sortedEmojis.forEach(sorted => {
|
||||
sorted.data.forEach(emojis =>
|
||||
emojis.forEach(emoji => {
|
||||
if (requestedIndex > 40) {
|
||||
return
|
||||
}
|
||||
prefetches.push({
|
||||
uri: reduceMotionEnabled ? emoji.static_url : emoji.url
|
||||
})
|
||||
requestedIndex++
|
||||
})
|
||||
)
|
||||
})
|
||||
try {
|
||||
FastImage.preload(prefetches)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
enabled?: boolean
|
||||
value?: string
|
||||
setValue:
|
||||
| Dispatch<SetStateAction<string | undefined>>
|
||||
| Dispatch<SetStateAction<string>>
|
||||
selectionRange: MutableRefObject<{
|
||||
start: number
|
||||
end: number
|
||||
}>
|
||||
}
|
||||
|
||||
const ComponentEmojis: React.FC<Props> = ({
|
||||
enabled = false,
|
||||
value,
|
||||
setValue,
|
||||
selectionRange,
|
||||
children
|
||||
}) => {
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
|
||||
const [emojisState, emojisDispatch] = useReducer(emojisReducer, {
|
||||
enabled,
|
||||
active: false,
|
||||
emojis: [],
|
||||
shortcode: null
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (emojisState.shortcode) {
|
||||
addEmoji(emojisState.shortcode)
|
||||
emojisDispatch({
|
||||
type: 'shortcode',
|
||||
payload: null
|
||||
})
|
||||
}
|
||||
}, [emojisState.shortcode])
|
||||
|
||||
const addEmoji = useCallback(
|
||||
(emojiShortcode: string) => {
|
||||
console.log(selectionRange.current)
|
||||
if (value?.length) {
|
||||
const contentFront = value.slice(0, selectionRange.current?.start)
|
||||
const contentRear = value.slice(selectionRange.current?.end)
|
||||
|
||||
const whiteSpaceRear = /\s/g.test(contentRear.slice(-1))
|
||||
|
||||
const newTextWithSpace = ` ${emojiShortcode}${
|
||||
whiteSpaceRear ? '' : ' '
|
||||
}`
|
||||
setValue([contentFront, newTextWithSpace, contentRear].join(''))
|
||||
} else {
|
||||
setValue(`${emojiShortcode} `)
|
||||
}
|
||||
},
|
||||
[value, selectionRange.current?.start, selectionRange.current?.end]
|
||||
)
|
||||
|
||||
const { data } = useEmojisQuery({ options: { enabled } })
|
||||
useEffect(() => {
|
||||
if (data && data.length) {
|
||||
let sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[] = []
|
||||
forEach(
|
||||
groupBy(sortBy(data, ['category', 'shortcode']), 'category'),
|
||||
(value, key) => sortedEmojis.push({ title: key, data: chunk(value, 5) })
|
||||
)
|
||||
emojisDispatch({
|
||||
type: 'load',
|
||||
payload: sortedEmojis
|
||||
})
|
||||
prefetchEmojis(sortedEmojis, reduceMotionEnabled)
|
||||
}
|
||||
}, [data, reduceMotionEnabled])
|
||||
|
||||
return (
|
||||
<EmojisContext.Provider
|
||||
value={{ emojisState, emojisDispatch }}
|
||||
children={children}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { ComponentEmojis, EmojisContext, EmojisButton, EmojisList }
|
Reference in New Issue
Block a user