mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Fixed #127
This commit is contained in:
@ -12,21 +12,7 @@ import React, {
|
|||||||
useReducer
|
useReducer
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image'
|
||||||
import EmojisContext, {
|
import EmojisContext, { emojisReducer } from './Emojis/helpers/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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const prefetchEmojis = (
|
const prefetchEmojis = (
|
||||||
sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[],
|
sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[],
|
||||||
|
@ -27,4 +27,15 @@ type ContextType = {
|
|||||||
}
|
}
|
||||||
const EmojisContext = createContext<ContextType>({} as ContextType)
|
const EmojisContext = createContext<ContextType>({} 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
|
export default EmojisContext
|
||||||
|
@ -3,7 +3,7 @@ import { useEmojisQuery } from '@utils/queryHooks/emojis'
|
|||||||
import { useSearchQuery } from '@utils/queryHooks/search'
|
import { useSearchQuery } from '@utils/queryHooks/search'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { forEach, groupBy, sortBy } from 'lodash'
|
import { chunk, forEach, groupBy, sortBy } from 'lodash'
|
||||||
import React, {
|
import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
@ -28,15 +28,17 @@ import ComposeContext from './utils/createContext'
|
|||||||
import ComposeDrafts from './Root/Drafts'
|
import ComposeDrafts from './Root/Drafts'
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image'
|
||||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
|
import { ComposeState } from './utils/types'
|
||||||
|
|
||||||
const prefetchEmojis = (
|
const prefetchEmojis = (
|
||||||
sortedEmojis: { title: string; data: Mastodon.Emoji[] }[],
|
sortedEmojis: NonNullable<ComposeState['emoji']['emojis']>,
|
||||||
reduceMotionEnabled: boolean
|
reduceMotionEnabled: boolean
|
||||||
) => {
|
) => {
|
||||||
const prefetches: { uri: string }[] = []
|
const prefetches: { uri: string }[] = []
|
||||||
let requestedIndex = 0
|
let requestedIndex = 0
|
||||||
sortedEmojis.forEach(sorted => {
|
sortedEmojis.forEach(sorted => {
|
||||||
sorted.data.forEach(emoji => {
|
sorted.data.forEach(emojis =>
|
||||||
|
emojis.forEach(emoji => {
|
||||||
if (requestedIndex > 40) {
|
if (requestedIndex > 40) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -45,6 +47,7 @@ const prefetchEmojis = (
|
|||||||
})
|
})
|
||||||
requestedIndex++
|
requestedIndex++
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
FastImage.preload(prefetches)
|
FastImage.preload(prefetches)
|
||||||
@ -90,10 +93,11 @@ const ComposeRoot = React.memo(
|
|||||||
const { data: emojisData } = useEmojisQuery({})
|
const { data: emojisData } = useEmojisQuery({})
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (emojisData && emojisData.length) {
|
if (emojisData && emojisData.length) {
|
||||||
let sortedEmojis: { title: string; data: Mastodon.Emoji[] }[] = []
|
let sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[] = []
|
||||||
forEach(
|
forEach(
|
||||||
groupBy(sortBy(emojisData, ['category', 'shortcode']), 'category'),
|
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({
|
composeDispatch({
|
||||||
type: 'emoji',
|
type: 'emoji',
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
import analytics from '@components/analytics'
|
|
||||||
import haptics from '@components/haptics'
|
import haptics from '@components/haptics'
|
||||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, {
|
import React, { RefObject, useCallback, useContext, useEffect } from 'react'
|
||||||
RefObject,
|
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useMemo
|
|
||||||
} from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
AccessibilityInfo,
|
AccessibilityInfo,
|
||||||
@ -25,52 +18,15 @@ import validUrl from 'valid-url'
|
|||||||
import updateText from '../../updateText'
|
import updateText from '../../updateText'
|
||||||
import ComposeContext from '../../utils/createContext'
|
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 (
|
|
||||||
<FastImage
|
|
||||||
accessibilityLabel={t('common:customEmoji.accessibilityLabel', {
|
|
||||||
emoji: emoji.shortcode
|
|
||||||
})}
|
|
||||||
accessibilityHint={t(
|
|
||||||
'screenCompose:content.root.footer.emojis.accessibilityHint'
|
|
||||||
)}
|
|
||||||
source={{ uri: reduceMotionEnabled ? emoji.static_url : emoji.url }}
|
|
||||||
style={styles.emoji}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
return (
|
|
||||||
<Pressable key={emoji.shortcode} onPress={onPress} children={children} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
accessibleRefEmojis: RefObject<SectionList>
|
accessibleRefEmojis: RefObject<SectionList>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
|
const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
|
||||||
const { composeState } = useContext(ComposeContext)
|
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||||
|
const { reduceMotionEnabled } = useAccessibility()
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tagEmojis = findNodeHandle(accessibleRefEmojis.current)
|
const tagEmojis = findNodeHandle(accessibleRefEmojis.current)
|
||||||
@ -86,21 +42,49 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
|
|||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
const emojiList = useCallback(
|
|
||||||
section =>
|
|
||||||
section.data.map((emoji: Mastodon.Emoji) => (
|
|
||||||
<SingleEmoji key={emoji.shortcode} emoji={emoji} />
|
|
||||||
)),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
const listItem = useCallback(
|
const listItem = useCallback(
|
||||||
({ section, index }) =>
|
({ index, item }: { item: Mastodon.Emoji[]; index: number }) => {
|
||||||
index === 0 ? (
|
return (
|
||||||
<View key={section.title} style={styles.emojis}>
|
<View key={index} style={styles.emojis}>
|
||||||
{emojiList(section)}
|
{item.map(emoji => {
|
||||||
|
const uri = reduceMotionEnabled ? emoji.static_url : emoji.url
|
||||||
|
if (validUrl.isHttpsUri(uri)) {
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
key={emoji.shortcode}
|
||||||
|
onPress={() => {
|
||||||
|
updateText({
|
||||||
|
composeState,
|
||||||
|
composeDispatch,
|
||||||
|
newText: `:${emoji.shortcode}:`,
|
||||||
|
type: 'emoji'
|
||||||
|
})
|
||||||
|
haptics('Light')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FastImage
|
||||||
|
accessibilityLabel={t(
|
||||||
|
'common:customEmoji.accessibilityLabel',
|
||||||
|
{
|
||||||
|
emoji: emoji.shortcode
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
accessibilityHint={t(
|
||||||
|
'screenCompose:content.root.footer.emojis.accessibilityHint'
|
||||||
|
)}
|
||||||
|
source={{ uri }}
|
||||||
|
style={styles.emoji}
|
||||||
|
/>
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})}
|
||||||
</View>
|
</View>
|
||||||
) : null,
|
)
|
||||||
[]
|
},
|
||||||
|
[composeState]
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -111,7 +95,7 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
|
|||||||
horizontal
|
horizontal
|
||||||
keyboardShouldPersistTaps='always'
|
keyboardShouldPersistTaps='always'
|
||||||
sections={composeState.emoji.emojis || []}
|
sections={composeState.emoji.emojis || []}
|
||||||
keyExtractor={item => item.shortcode}
|
keyExtractor={item => item[0].shortcode}
|
||||||
renderSectionHeader={listHeader}
|
renderSectionHeader={listHeader}
|
||||||
renderItem={listItem}
|
renderItem={listItem}
|
||||||
windowSize={2}
|
windowSize={2}
|
||||||
|
2
src/screens/Compose/utils/types.d.ts
vendored
2
src/screens/Compose/utils/types.d.ts
vendored
@ -40,7 +40,7 @@ export type ComposeState = {
|
|||||||
}
|
}
|
||||||
emoji: {
|
emoji: {
|
||||||
active: boolean
|
active: boolean
|
||||||
emojis: { title: string; data: Mastodon.Emoji[] }[] | undefined
|
emojis: { title: string; data: Mastodon.Emoji[][] }[] | undefined
|
||||||
}
|
}
|
||||||
poll: {
|
poll: {
|
||||||
active: boolean
|
active: boolean
|
||||||
|
Reference in New Issue
Block a user