diff --git a/src/components/Emojis.tsx b/src/components/Emojis.tsx index d7192b73..7854eeca 100644 --- a/src/components/Emojis.tsx +++ b/src/components/Emojis.tsx @@ -12,21 +12,7 @@ import React, { useReducer } from 'react' import FastImage from 'react-native-fast-image' -import EmojisContext, { - EmojisAction, - EmojisState -} from './Emojis/helpers/EmojisContext' - -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 } - } -} +import EmojisContext, { emojisReducer } from './Emojis/helpers/EmojisContext' const prefetchEmojis = ( sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[], diff --git a/src/components/Emojis/helpers/EmojisContext.tsx b/src/components/Emojis/helpers/EmojisContext.tsx index 97282085..2e4f5458 100644 --- a/src/components/Emojis/helpers/EmojisContext.tsx +++ b/src/components/Emojis/helpers/EmojisContext.tsx @@ -27,4 +27,15 @@ type ContextType = { } const EmojisContext = createContext({} as ContextType) +export 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 } + } +} + export default EmojisContext diff --git a/src/screens/Compose/Root.tsx b/src/screens/Compose/Root.tsx index 951355e0..742b5f62 100644 --- a/src/screens/Compose/Root.tsx +++ b/src/screens/Compose/Root.tsx @@ -3,7 +3,7 @@ import { useEmojisQuery } from '@utils/queryHooks/emojis' import { useSearchQuery } from '@utils/queryHooks/search' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import { forEach, groupBy, sortBy } from 'lodash' +import { chunk, forEach, groupBy, sortBy } from 'lodash' import React, { useCallback, useContext, @@ -28,23 +28,26 @@ import ComposeContext from './utils/createContext' import ComposeDrafts from './Root/Drafts' import FastImage from 'react-native-fast-image' import { useAccessibility } from '@utils/accessibility/AccessibilityManager' +import { ComposeState } from './utils/types' const prefetchEmojis = ( - sortedEmojis: { title: string; data: Mastodon.Emoji[] }[], + sortedEmojis: NonNullable, reduceMotionEnabled: boolean ) => { const prefetches: { uri: string }[] = [] let requestedIndex = 0 sortedEmojis.forEach(sorted => { - sorted.data.forEach(emoji => { - if (requestedIndex > 40) { - return - } - prefetches.push({ - uri: reduceMotionEnabled ? emoji.static_url : emoji.url + sorted.data.forEach(emojis => + emojis.forEach(emoji => { + if (requestedIndex > 40) { + return + } + prefetches.push({ + uri: reduceMotionEnabled ? emoji.static_url : emoji.url + }) + requestedIndex++ }) - requestedIndex++ - }) + ) }) try { FastImage.preload(prefetches) @@ -90,10 +93,11 @@ const ComposeRoot = React.memo( const { data: emojisData } = useEmojisQuery({}) useEffect(() => { if (emojisData && emojisData.length) { - let sortedEmojis: { title: string; data: Mastodon.Emoji[] }[] = [] + let sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[] = [] forEach( groupBy(sortBy(emojisData, ['category', 'shortcode']), 'category'), - (value, key) => sortedEmojis.push({ title: key, data: value }) + (value, key) => + sortedEmojis.push({ title: key, data: chunk(value, 5) }) ) composeDispatch({ type: 'emoji', diff --git a/src/screens/Compose/Root/Footer/Emojis.tsx b/src/screens/Compose/Root/Footer/Emojis.tsx index e1ff6fb3..bf9a5ea2 100644 --- a/src/screens/Compose/Root/Footer/Emojis.tsx +++ b/src/screens/Compose/Root/Footer/Emojis.tsx @@ -1,15 +1,8 @@ -import analytics from '@components/analytics' import haptics from '@components/haptics' import { useAccessibility } from '@utils/accessibility/AccessibilityManager' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import React, { - RefObject, - useCallback, - useContext, - useEffect, - useMemo -} from 'react' +import React, { RefObject, useCallback, useContext, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { AccessibilityInfo, @@ -25,52 +18,15 @@ import validUrl from 'valid-url' import updateText from '../../updateText' import ComposeContext from '../../utils/createContext' -const SingleEmoji = ({ emoji }: { emoji: Mastodon.Emoji }) => { - const { t } = useTranslation() - const { reduceMotionEnabled } = useAccessibility() - - const { composeState, composeDispatch } = useContext(ComposeContext) - const onPress = useCallback(() => { - analytics('compose_emoji_add') - updateText({ - composeState, - composeDispatch, - newText: `:${emoji.shortcode}:`, - type: 'emoji' - }) - haptics('Light') - }, [composeState]) - const children = useMemo(() => { - const uri = reduceMotionEnabled ? emoji.static_url : emoji.url - if (validUrl.isHttpsUri(uri)) { - return ( - - ) - } else { - return null - } - }, []) - return ( - - ) -} - export interface Props { accessibleRefEmojis: RefObject } const ComposeEmojis: React.FC = ({ accessibleRefEmojis }) => { - const { composeState } = useContext(ComposeContext) + const { composeState, composeDispatch } = useContext(ComposeContext) + const { reduceMotionEnabled } = useAccessibility() const { theme } = useTheme() + const { t } = useTranslation() useEffect(() => { const tagEmojis = findNodeHandle(accessibleRefEmojis.current) @@ -86,21 +42,49 @@ const ComposeEmojis: React.FC = ({ accessibleRefEmojis }) => { [] ) - const emojiList = useCallback( - section => - section.data.map((emoji: Mastodon.Emoji) => ( - - )), - [] - ) const listItem = useCallback( - ({ section, index }) => - index === 0 ? ( - - {emojiList(section)} + ({ index, item }: { item: Mastodon.Emoji[]; index: number }) => { + return ( + + {item.map(emoji => { + const uri = reduceMotionEnabled ? emoji.static_url : emoji.url + if (validUrl.isHttpsUri(uri)) { + return ( + { + updateText({ + composeState, + composeDispatch, + newText: `:${emoji.shortcode}:`, + type: 'emoji' + }) + haptics('Light') + }} + > + + + ) + } else { + return null + } + })} - ) : null, - [] + ) + }, + [composeState] ) return ( @@ -111,7 +95,7 @@ const ComposeEmojis: React.FC = ({ accessibleRefEmojis }) => { horizontal keyboardShouldPersistTaps='always' sections={composeState.emoji.emojis || []} - keyExtractor={item => item.shortcode} + keyExtractor={item => item[0].shortcode} renderSectionHeader={listHeader} renderItem={listItem} windowSize={2} diff --git a/src/screens/Compose/utils/types.d.ts b/src/screens/Compose/utils/types.d.ts index 713eae6f..866286ae 100644 --- a/src/screens/Compose/utils/types.d.ts +++ b/src/screens/Compose/utils/types.d.ts @@ -40,7 +40,7 @@ export type ComposeState = { } emoji: { active: boolean - emojis: { title: string; data: Mastodon.Emoji[] }[] | undefined + emojis: { title: string; data: Mastodon.Emoji[][] }[] | undefined } poll: { active: boolean