From b0b7a7567b4b0a8bd206089196667e93722ce0f3 Mon Sep 17 00:00:00 2001 From: Zhiyuan Zheng Date: Sat, 27 Mar 2021 00:02:32 +0100 Subject: [PATCH] Added reduced motion --- src/App.tsx | 9 ++-- src/components/Parse/Emojis.tsx | 14 +++-- src/components/Timeline/Shared/Avatar.tsx | 5 +- src/screens/Announcements.tsx | 32 +++++++----- src/screens/Compose/Root.tsx | 26 +++++----- src/screens/Compose/Root/Footer/Emojis.tsx | 18 +++---- src/screens/Tabs/Shared/Account/Header.tsx | 8 ++- .../Shared/Account/Information/Avatar.tsx | 8 ++- .../accessibility/AccessibilityManager.tsx | 52 +++++++++++++++++++ src/utils/styles/ThemeManager.tsx | 2 +- 10 files changed, 127 insertions(+), 47 deletions(-) create mode 100644 src/utils/accessibility/AccessibilityManager.tsx diff --git a/src/App.tsx b/src/App.tsx index 2503520c..0ef71933 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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 ( - - - + + + + + ) } else { diff --git a/src/components/Parse/Emojis.tsx b/src/components/Parse/Emojis.tsx index 9e2e3130..ab9ffffd 100644 --- a/src/components/Parse/Emojis.tsx +++ b/src/components/Parse/Emojis.tsx @@ -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( {/* When emoji starts a paragraph, lineHeight will break */} {i === 0 ? : null} - diff --git a/src/components/Timeline/Shared/Avatar.tsx b/src/components/Timeline/Shared/Avatar.tsx index 54f6b722..0372640d 100644 --- a/src/components/Timeline/Shared/Avatar.tsx +++ b/src/components/Timeline/Shared/Avatar.tsx @@ -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' }} /> ) diff --git a/src/screens/Announcements.tsx b/src/screens/Announcements.tsx index 555b539f..b25b952d 100644 --- a/src/screens/Announcements.tsx +++ b/src/screens/Announcements.tsx @@ -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 = ({ }, 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 = ({ 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 = ({ }} > {reaction.url ? ( - ) : ( @@ -130,7 +132,10 @@ const ScreenAnnouncements: React.FC = ({ )} {reaction.count ? ( {reaction.count} @@ -246,7 +251,8 @@ const ScreenAnnouncements: React.FC = ({ 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 } diff --git a/src/screens/Compose/Root.tsx b/src/screens/Compose/Root.tsx index 8ed0369a..fea1bff6 100644 --- a/src/screens/Compose/Root.tsx +++ b/src/screens/Compose/Root.tsx @@ -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) { diff --git a/src/screens/Compose/Root/Footer/Emojis.tsx b/src/screens/Compose/Root/Footer/Emojis.tsx index 807370a8..0be2886b 100644 --- a/src/screens/Compose/Root/Footer/Emojis.tsx +++ b/src/screens/Compose/Root/Footer/Emojis.tsx @@ -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( () => ( - ), @@ -81,6 +78,7 @@ const ComposeEmojis: React.FC = () => { keyExtractor={item => item.shortcode} renderSectionHeader={listHeader} renderItem={listItem} + windowSize={3} /> ) diff --git a/src/screens/Tabs/Shared/Account/Header.tsx b/src/screens/Tabs/Shared/Account/Header.tsx index 48f3ee40..d82cfb27 100644 --- a/src/screens/Tabs/Shared/Account/Header.tsx +++ b/src/screens/Tabs/Shared/Account/Header.tsx @@ -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 = ({ account, limitHeight = false }) => { +const AccountHeader: React.FC = ({ account }) => { const { accountState } = useContext(AccountContext) + const { reduceMotionEnabled } = useAccessibility() const { theme } = useTheme() const topInset = useSafeAreaInsets().top return ( = ({ account, myInfo }) => { const navigation = useNavigation< StackNavigationProp >() + const { reduceMotionEnabled } = useAccessibility() return ( = ({ account, myInfo }) => { ) diff --git a/src/utils/accessibility/AccessibilityManager.tsx b/src/utils/accessibility/AccessibilityManager.tsx new file mode 100644 index 00000000..510cf7c9 --- /dev/null +++ b/src/utils/accessibility/AccessibilityManager.tsx @@ -0,0 +1,52 @@ +import React, { createContext, useContext, useEffect, useState } from 'react' +import { AccessibilityInfo } from 'react-native' + +type ContextType = { + reduceMotionEnabled: boolean +} + +const AccessibilityContext = createContext({ + 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 ( + + {children} + + ) +} + +export default AccessibilityManager diff --git a/src/utils/styles/ThemeManager.tsx b/src/utils/styles/ThemeManager.tsx index eaead5d3..4a739d99 100644 --- a/src/utils/styles/ThemeManager.tsx +++ b/src/utils/styles/ThemeManager.tsx @@ -11,7 +11,7 @@ type ContextType = { setTheme: (theme: 'light' | 'dark') => void } -export const ManageThemeContext = createContext({ +const ManageThemeContext = createContext({ mode: 'light', theme: getTheme('light'), setTheme: () => {}