Merge pull request #87 from tooot-app/main

Nightly 210326
This commit is contained in:
xmflsct 2021-03-27 01:36:02 +01:00 committed by GitHub
commit df1a334bb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 155 additions and 65 deletions

View File

@ -58,9 +58,10 @@ declare namespace Nav {
imageUrls: {
id: Mastodon.Attachment['id']
url: Mastodon.AttachmentImage['url']
remote_url?: Mastodon.AttachmentImage['remote_url']
blurhash: Mastodon.AttachmentImage['blurhash']
width?: number
height?: number
remote_url?: Mastodon.AttachmentImage['remote_url']
}[]
id: Mastodon.Attachment['id']
}

View File

@ -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 {

View File

@ -102,11 +102,7 @@ const GracefullyImage = React.memo(
return (
<Pressable
style={[
style,
dimension,
{ backgroundColor: theme.backgroundOverlayDefault }
]}
style={[style, dimension, { backgroundColor: theme.shimmerDefault }]}
{...(onPress
? hidden
? { disabled: true }
@ -130,9 +126,7 @@ const styles = StyleSheet.create({
blurhash: {
width: '100%',
height: '100%',
position: 'absolute',
top: StyleConstants.Spacing.XS / 2,
left: StyleConstants.Spacing.XS / 2
position: 'absolute'
}
})

View File

@ -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>

View File

@ -235,7 +235,7 @@ const ParseHTML = React.memo(
const [expanded, setExpanded] = useState(false)
const onTextLayout = useCallback(({ nativeEvent }) => {
if (nativeEvent.lines.length >= numberOfLines) {
if (nativeEvent.lines.length >= numberOfLines + 5) {
setExpandAllow(true)
}
}, [])
@ -245,7 +245,9 @@ const ParseHTML = React.memo(
<Text
children={children}
onTextLayout={onTextLayout}
numberOfLines={expanded ? 999 : numberOfLines}
numberOfLines={
expandAllow ? (expanded ? 999 : numberOfLines) : undefined
}
/>
{expandAllow ? (
<Pressable

View File

@ -49,6 +49,7 @@ const TimelineAttachment = React.memo(
id: attachment.id,
url: attachment.url,
remote_url: attachment.remote_url,
blurhash: attachment.blurhash,
width: attachment.meta?.original?.width,
height: attachment.meta?.original?.height
})

View File

@ -1,7 +1,7 @@
import Button from '@components/Button'
import { StyleConstants } from '@utils/styles/constants'
import { Video } from 'expo-av'
import React, { useCallback, useRef, useState } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { Pressable, StyleSheet, View } from 'react-native'
import { Blurhash } from 'react-native-blurhash'
import attachmentAspectRatio from './aspectRatio'
@ -54,6 +54,12 @@ const AttachmentVideo: React.FC<Props> = ({
})
}, [videoLoaded, videoPosition])
useEffect(() => {
if (gifv) {
videoPlayer.current?.setIsLoopingAsync(true)
}
}, [])
return (
<View
style={[
@ -68,10 +74,14 @@ const AttachmentVideo: React.FC<Props> = ({
height: '100%',
opacity: sensitiveShown ? 0 : 1
}}
resizeMode='cover'
usePoster
posterSource={{ uri: video.preview_url }}
posterStyle={{ resizeMode: 'cover' }}
{...(gifv
? { shouldPlay: true, source: { uri: video.url } }
: {
resizeMode: 'cover',
posterSource: { uri: video.preview_url },
posterStyle: { resizeMode: 'cover' }
})}
useNativeControls={false}
onFullscreenUpdate={event => {
if (event.fullscreenUpdate === 3) {
@ -94,7 +104,7 @@ const AttachmentVideo: React.FC<Props> = ({
}}
/>
) : null
) : (
) : !gifv ? (
<Button
round
overlay
@ -104,7 +114,7 @@ const AttachmentVideo: React.FC<Props> = ({
onPress={playOnPress}
loading={videoLoading}
/>
)}
) : null}
</Pressable>
</View>
)

View File

@ -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'
}}
/>
)

View File

@ -229,7 +229,7 @@ const TimelinePoll: React.FC<Props> = ({
style={styles.optionContainer}
onPress={() => {
analytics('timeline_shared_vote_option_press')
haptics('Light')
!allOptions[index] && haptics('Light')
if (poll.multiple) {
setAllOptions(allOptions.map((o, i) => (i === index ? !o : o)))
} else {

View File

@ -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
}

View File

@ -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) {

View File

@ -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>
)

View File

@ -93,6 +93,7 @@ const ImageItem = ({
original: imageSrc.url,
remote: imageSrc.remote_url
}}
blurhash={imageSrc.blurhash}
{...((!imageSrc.width || !imageSrc.height) && {
setImageDimensions
})}

View File

@ -148,6 +148,7 @@ const ImageItem = ({
original: imageSrc.url,
remote: imageSrc.remote_url
}}
blurhash={imageSrc.blurhash}
{...((!imageSrc.width || !imageSrc.height) && {
setImageDimensions
})}

View File

@ -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,

View File

@ -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>
)

View File

@ -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

View File

@ -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: () => {}