mirror of https://github.com/tooot-app/app
Added reduced motion
This commit is contained in:
parent
d490cae955
commit
b0b7a7567b
|
@ -7,6 +7,7 @@ import log from '@root/startup/log'
|
|||
import netInfo from '@root/startup/netInfo'
|
||||
import sentry from '@root/startup/sentry'
|
||||
import { persistor, store } from '@root/store'
|
||||
import AccessibilityManager from '@utils/accessibility/AccessibilityManager'
|
||||
import { getSettingsLanguage } from '@utils/slices/settingsSlice'
|
||||
import ThemeManager from '@utils/styles/ThemeManager'
|
||||
import * as Notifications from 'expo-notifications'
|
||||
|
@ -90,9 +91,11 @@ const App: React.FC = () => {
|
|||
i18n.changeLanguage(getSettingsLanguage(store.getState()))
|
||||
return (
|
||||
<ActionSheetProvider>
|
||||
<ThemeManager>
|
||||
<Screens localCorrupt={localCorrupt} />
|
||||
</ThemeManager>
|
||||
<AccessibilityManager>
|
||||
<ThemeManager>
|
||||
<Screens localCorrupt={localCorrupt} />
|
||||
</ThemeManager>
|
||||
</AccessibilityManager>
|
||||
</ActionSheetProvider>
|
||||
)
|
||||
} else {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { getSettingsFontsize } from '@utils/slices/settingsSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { adaptiveScale } from '@utils/styles/scaling'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Image, StyleSheet, Text } from 'react-native'
|
||||
import { StyleSheet, Text } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
const regexEmoji = new RegExp(/(:[A-Za-z0-9_]+:)/)
|
||||
|
@ -24,6 +26,8 @@ const ParseEmojis = React.memo(
|
|||
adaptiveSize = false,
|
||||
fontBold = false
|
||||
}: Props) => {
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
|
||||
const adaptiveFontsize = useSelector(getSettingsFontsize)
|
||||
const adaptedFontsize = adaptiveScale(
|
||||
StyleConstants.Font.Size[size],
|
||||
|
@ -69,9 +73,13 @@ const ParseEmojis = React.memo(
|
|||
<Text key={i}>
|
||||
{/* When emoji starts a paragraph, lineHeight will break */}
|
||||
{i === 0 ? <Text> </Text> : null}
|
||||
<Image
|
||||
<FastImage
|
||||
key={adaptiveFontsize}
|
||||
source={{ uri: emojis[emojiIndex].url }}
|
||||
source={{
|
||||
uri: reduceMotionEnabled
|
||||
? emojis[emojiIndex].static_url
|
||||
: emojis[emojiIndex].url
|
||||
}}
|
||||
style={styles.image}
|
||||
/>
|
||||
</Text>
|
||||
|
|
|
@ -33,9 +33,10 @@ const TimelineAvatar = React.memo(
|
|||
height: StyleConstants.Avatar.M
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
borderRadius: StyleConstants.Avatar.M,
|
||||
overflow: 'hidden',
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
marginRight: StyleConstants.Spacing.S,
|
||||
backgroundColor: 'red'
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -6,6 +6,7 @@ import { ParseHTML } from '@components/Parse'
|
|||
import RelativeTime from '@components/RelativeTime'
|
||||
import { BlurView } from '@react-native-community/blur'
|
||||
import { StackScreenProps } from '@react-navigation/stack'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import {
|
||||
useAnnouncementMutation,
|
||||
useAnnouncementQuery
|
||||
|
@ -14,15 +15,9 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import {
|
||||
Dimensions,
|
||||
Image,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View
|
||||
} from 'react-native'
|
||||
import { Dimensions, Pressable, StyleSheet, Text, View } from 'react-native'
|
||||
import { Circle } from 'react-native-animated-spinkit'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { FlatList, ScrollView } from 'react-native-gesture-handler'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
|
||||
|
@ -37,6 +32,7 @@ const ScreenAnnouncements: React.FC<ScreenAnnouncementsProp> = ({
|
|||
},
|
||||
navigation
|
||||
}) => {
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
const { mode, theme } = useTheme()
|
||||
const [index, setIndex] = useState(0)
|
||||
const { t } = useTranslation('sharedAnnouncements')
|
||||
|
@ -102,7 +98,9 @@ const ScreenAnnouncements: React.FC<ScreenAnnouncementsProp> = ({
|
|||
style={[
|
||||
styles.reaction,
|
||||
{
|
||||
borderColor: reaction.me ? theme.disabled : theme.primaryDefault,
|
||||
borderColor: reaction.me
|
||||
? theme.disabled
|
||||
: theme.primaryDefault,
|
||||
backgroundColor: reaction.me
|
||||
? theme.disabled
|
||||
: theme.backgroundDefault
|
||||
|
@ -121,8 +119,12 @@ const ScreenAnnouncements: React.FC<ScreenAnnouncementsProp> = ({
|
|||
}}
|
||||
>
|
||||
{reaction.url ? (
|
||||
<Image
|
||||
source={{ uri: reaction.url }}
|
||||
<FastImage
|
||||
source={{
|
||||
uri: reduceMotionEnabled
|
||||
? reaction.static_url
|
||||
: reaction.url
|
||||
}}
|
||||
style={[styles.reactionImage]}
|
||||
/>
|
||||
) : (
|
||||
|
@ -130,7 +132,10 @@ const ScreenAnnouncements: React.FC<ScreenAnnouncementsProp> = ({
|
|||
)}
|
||||
{reaction.count ? (
|
||||
<Text
|
||||
style={[styles.reactionCount, { color: theme.primaryDefault }]}
|
||||
style={[
|
||||
styles.reactionCount,
|
||||
{ color: theme.primaryDefault }
|
||||
]}
|
||||
>
|
||||
{reaction.count}
|
||||
</Text>
|
||||
|
@ -246,7 +251,8 @@ const ScreenAnnouncements: React.FC<ScreenAnnouncementsProp> = ({
|
|||
styles.indicator,
|
||||
{
|
||||
borderColor: theme.primaryDefault,
|
||||
backgroundColor: i === index ? theme.primaryDefault : undefined,
|
||||
backgroundColor:
|
||||
i === index ? theme.primaryDefault : undefined,
|
||||
marginLeft:
|
||||
i === query.data.length ? 0 : StyleConstants.Spacing.S
|
||||
}
|
||||
|
|
|
@ -4,14 +4,8 @@ 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 React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef
|
||||
} from 'react'
|
||||
import { FlatList, Image, StyleSheet, View } from 'react-native'
|
||||
import React, { useCallback, useContext, useEffect, useMemo } from 'react'
|
||||
import { FlatList, StyleSheet, View } from 'react-native'
|
||||
import { Circle } from 'react-native-animated-spinkit'
|
||||
import ComposeActions from './Root/Actions'
|
||||
import ComposePosting from './Posting'
|
||||
|
@ -20,24 +14,32 @@ import ComposeRootHeader from './Root/Header'
|
|||
import ComposeRootSuggestion from './Root/Suggestion'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import ComposeDrafts from './Root/Drafts'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
|
||||
const prefetchEmojis = (
|
||||
sortedEmojis: { title: string; data: Mastodon.Emoji[] }[]
|
||||
sortedEmojis: { title: string; data: Mastodon.Emoji[] }[],
|
||||
reduceMotionEnabled: boolean
|
||||
) => {
|
||||
const prefetches: { uri: string }[] = []
|
||||
let requestedIndex = 0
|
||||
sortedEmojis.forEach(sorted => {
|
||||
sorted.data.forEach(emoji => {
|
||||
if (requestedIndex > 40) {
|
||||
return
|
||||
}
|
||||
Image.prefetch(emoji.url)
|
||||
prefetches.push({
|
||||
uri: reduceMotionEnabled ? emoji.static_url : emoji.url
|
||||
})
|
||||
requestedIndex++
|
||||
})
|
||||
})
|
||||
FastImage.preload(prefetches)
|
||||
}
|
||||
|
||||
const ComposeRoot = React.memo(
|
||||
() => {
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
const { theme } = useTheme()
|
||||
|
||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||
|
@ -74,9 +76,9 @@ const ComposeRoot = React.memo(
|
|||
type: 'emoji',
|
||||
payload: { ...composeState.emoji, emojis: sortedEmojis }
|
||||
})
|
||||
prefetchEmojis(sortedEmojis)
|
||||
prefetchEmojis(sortedEmojis, reduceMotionEnabled)
|
||||
}
|
||||
}, [emojisData])
|
||||
}, [emojisData, reduceMotionEnabled])
|
||||
|
||||
const listEmpty = useMemo(() => {
|
||||
if (isFetching) {
|
||||
|
|
|
@ -3,18 +3,15 @@ import haptics from '@components/haptics'
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useContext, useMemo } from 'react'
|
||||
import {
|
||||
Image,
|
||||
Pressable,
|
||||
SectionList,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View
|
||||
} from 'react-native'
|
||||
import { Pressable, SectionList, StyleSheet, Text, View } from 'react-native'
|
||||
import ComposeContext from '../../utils/createContext'
|
||||
import updateText from '../../updateText'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
|
||||
const SingleEmoji = ({ emoji }: { emoji: Mastodon.Emoji }) => {
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
|
||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||
const onPress = useCallback(() => {
|
||||
analytics('compose_emoji_add')
|
||||
|
@ -32,8 +29,8 @@ const SingleEmoji = ({ emoji }: { emoji: Mastodon.Emoji }) => {
|
|||
}, [composeState])
|
||||
const children = useMemo(
|
||||
() => (
|
||||
<Image
|
||||
source={{ uri: emoji.url, cache: 'force-cache' }}
|
||||
<FastImage
|
||||
source={{ uri: reduceMotionEnabled ? emoji.static_url : emoji.url }}
|
||||
style={styles.emoji}
|
||||
/>
|
||||
),
|
||||
|
@ -81,6 +78,7 @@ const ComposeEmojis: React.FC = () => {
|
|||
keyExtractor={item => item.shortcode}
|
||||
renderSectionHeader={listHeader}
|
||||
renderItem={listItem}
|
||||
windowSize={3}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext } from 'react'
|
||||
import { Dimensions, Image } from 'react-native'
|
||||
|
@ -9,14 +10,17 @@ export interface Props {
|
|||
limitHeight?: boolean
|
||||
}
|
||||
|
||||
const AccountHeader: React.FC<Props> = ({ account, limitHeight = false }) => {
|
||||
const AccountHeader: React.FC<Props> = ({ account }) => {
|
||||
const { accountState } = useContext(AccountContext)
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
const { theme } = useTheme()
|
||||
const topInset = useSafeAreaInsets().top
|
||||
|
||||
return (
|
||||
<Image
|
||||
source={{ uri: account?.header }}
|
||||
source={{
|
||||
uri: reduceMotionEnabled ? account?.header_static : account?.header
|
||||
}}
|
||||
style={{
|
||||
height:
|
||||
Dimensions.get('screen').width * accountState.headerRatio + topInset,
|
||||
|
|
|
@ -2,6 +2,7 @@ import analytics from '@components/analytics'
|
|||
import GracefullyImage from '@components/GracefullyImage'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { StackNavigationProp } from '@react-navigation/stack'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import React from 'react'
|
||||
import { Pressable, StyleSheet } from 'react-native'
|
||||
|
@ -15,6 +16,7 @@ const AccountInformationAvatar: React.FC<Props> = ({ account, myInfo }) => {
|
|||
const navigation = useNavigation<
|
||||
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||
>()
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
|
@ -28,7 +30,11 @@ const AccountInformationAvatar: React.FC<Props> = ({ account, myInfo }) => {
|
|||
<GracefullyImage
|
||||
key={account?.avatar}
|
||||
style={styles.image}
|
||||
uri={{ original: account?.avatar }}
|
||||
uri={{
|
||||
original: reduceMotionEnabled
|
||||
? account?.avatar_static
|
||||
: account?.avatar
|
||||
}}
|
||||
/>
|
||||
</Pressable>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import React, { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { AccessibilityInfo } from 'react-native'
|
||||
|
||||
type ContextType = {
|
||||
reduceMotionEnabled: boolean
|
||||
}
|
||||
|
||||
const AccessibilityContext = createContext<ContextType>({
|
||||
reduceMotionEnabled: false
|
||||
})
|
||||
|
||||
export const useAccessibility = () => useContext(AccessibilityContext)
|
||||
|
||||
const AccessibilityManager: React.FC = ({ children }) => {
|
||||
const [reduceMotionEnabled, setReduceMotionEnabled] = useState(false)
|
||||
|
||||
const handleReduceMotionChanged = (reduceMotionEnabled: boolean) =>
|
||||
setReduceMotionEnabled(reduceMotionEnabled)
|
||||
|
||||
const loadReduceMotion = async () => {
|
||||
const reduceMotion = await AccessibilityInfo.isReduceMotionEnabled()
|
||||
setReduceMotionEnabled(reduceMotion)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadReduceMotion()
|
||||
|
||||
AccessibilityInfo.addEventListener(
|
||||
'reduceMotionChanged',
|
||||
handleReduceMotionChanged
|
||||
)
|
||||
|
||||
return () => {
|
||||
AccessibilityInfo.removeEventListener(
|
||||
'reduceMotionChanged',
|
||||
handleReduceMotionChanged
|
||||
)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<AccessibilityContext.Provider
|
||||
value={{
|
||||
reduceMotionEnabled
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AccessibilityContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccessibilityManager
|
|
@ -11,7 +11,7 @@ type ContextType = {
|
|||
setTheme: (theme: 'light' | 'dark') => void
|
||||
}
|
||||
|
||||
export const ManageThemeContext = createContext<ContextType>({
|
||||
const ManageThemeContext = createContext<ContextType>({
|
||||
mode: 'light',
|
||||
theme: getTheme('light'),
|
||||
setTheme: () => {}
|
||||
|
|
Loading…
Reference in New Issue