mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Refine accessibility
This commit is contained in:
@ -31,6 +31,7 @@ const ComponentAccount: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole='button'
|
||||
style={[styles.itemDefault, styles.itemAccount]}
|
||||
onPress={customOnPress || onPress}
|
||||
>
|
||||
|
@ -4,6 +4,7 @@ import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useEffect, useMemo, useRef } from 'react'
|
||||
import {
|
||||
AccessibilityProps,
|
||||
Pressable,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
@ -14,15 +15,18 @@ import {
|
||||
import { Flow } from 'react-native-animated-spinkit'
|
||||
|
||||
export interface Props {
|
||||
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
|
||||
accessibilityHint?: AccessibilityProps['accessibilityHint']
|
||||
|
||||
style?: StyleProp<ViewStyle>
|
||||
|
||||
type: 'icon' | 'text'
|
||||
content: string
|
||||
|
||||
selected?: boolean
|
||||
loading?: boolean
|
||||
destructive?: boolean
|
||||
disabled?: boolean
|
||||
active?: boolean
|
||||
|
||||
strokeWidth?: number
|
||||
size?: 'S' | 'M' | 'L'
|
||||
@ -34,13 +38,15 @@ export interface Props {
|
||||
}
|
||||
|
||||
const Button: React.FC<Props> = ({
|
||||
accessibilityLabel,
|
||||
accessibilityHint,
|
||||
style: customStyle,
|
||||
type,
|
||||
content,
|
||||
selected,
|
||||
loading = false,
|
||||
destructive = false,
|
||||
disabled = false,
|
||||
active = false,
|
||||
strokeWidth,
|
||||
size = 'M',
|
||||
spacing = 'S',
|
||||
@ -57,7 +63,7 @@ const Button: React.FC<Props> = ({
|
||||
} else {
|
||||
mounted.current = true
|
||||
}
|
||||
}, [content, loading, disabled, active])
|
||||
}, [content, loading, disabled])
|
||||
|
||||
const loadingSpinkit = useMemo(
|
||||
() => (
|
||||
@ -68,40 +74,22 @@ const Button: React.FC<Props> = ({
|
||||
[mode]
|
||||
)
|
||||
|
||||
const colorContent = useMemo(() => {
|
||||
if (active) {
|
||||
const mainColor = useMemo(() => {
|
||||
if (selected) {
|
||||
return theme.blue
|
||||
} else if (overlay) {
|
||||
return theme.primaryOverlay
|
||||
} else if (disabled || loading) {
|
||||
return theme.disabled
|
||||
} else {
|
||||
if (overlay) {
|
||||
return theme.primaryOverlay
|
||||
if (destructive) {
|
||||
return theme.red
|
||||
} else {
|
||||
if (disabled) {
|
||||
return theme.secondary
|
||||
} else {
|
||||
if (destructive) {
|
||||
return theme.red
|
||||
} else {
|
||||
return theme.primaryDefault
|
||||
}
|
||||
}
|
||||
return theme.primaryDefault
|
||||
}
|
||||
}
|
||||
}, [mode, disabled])
|
||||
const colorBorder = useMemo(() => {
|
||||
if (active) {
|
||||
return theme.blue
|
||||
} else {
|
||||
if (disabled || loading) {
|
||||
return theme.secondary
|
||||
} else {
|
||||
if (destructive) {
|
||||
return theme.red
|
||||
} else {
|
||||
return theme.primaryDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [mode, loading, disabled])
|
||||
}, [mode, disabled, loading, selected])
|
||||
|
||||
const colorBackground = useMemo(() => {
|
||||
if (overlay) {
|
||||
return theme.backgroundOverlayInvert
|
||||
@ -117,7 +105,7 @@ const Button: React.FC<Props> = ({
|
||||
<>
|
||||
<Icon
|
||||
name={content}
|
||||
color={colorContent}
|
||||
color={mainColor}
|
||||
strokeWidth={strokeWidth}
|
||||
style={{ opacity: loading ? 0 : 1 }}
|
||||
size={StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1)}
|
||||
@ -130,7 +118,7 @@ const Button: React.FC<Props> = ({
|
||||
<>
|
||||
<Text
|
||||
style={{
|
||||
color: colorContent,
|
||||
color: mainColor,
|
||||
fontSize:
|
||||
StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1),
|
||||
fontWeight: destructive
|
||||
@ -145,7 +133,7 @@ const Button: React.FC<Props> = ({
|
||||
</>
|
||||
)
|
||||
}
|
||||
}, [mode, content, loading, disabled, active])
|
||||
}, [mode, content, loading, disabled])
|
||||
|
||||
enum spacingMapping {
|
||||
XS = 'S',
|
||||
@ -156,11 +144,20 @@ const Button: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessible
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityHint={accessibilityHint}
|
||||
accessibilityRole='button'
|
||||
accessibilityState={{
|
||||
selected,
|
||||
disabled: disabled || selected,
|
||||
busy: loading
|
||||
}}
|
||||
style={[
|
||||
styles.button,
|
||||
{
|
||||
borderWidth: overlay ? 0 : 1,
|
||||
borderColor: colorBorder,
|
||||
borderColor: mainColor,
|
||||
backgroundColor: colorBackground,
|
||||
paddingVertical: StyleConstants.Spacing[spacing],
|
||||
paddingHorizontal:
|
||||
@ -171,7 +168,7 @@ const Button: React.FC<Props> = ({
|
||||
testID='base'
|
||||
onPress={onPress}
|
||||
children={children}
|
||||
disabled={disabled || active || loading}
|
||||
disabled={selected || disabled || loading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import {
|
||||
AccessibilityProps,
|
||||
Image,
|
||||
ImageStyle,
|
||||
Pressable,
|
||||
@ -18,6 +19,9 @@ import { Blurhash } from 'react-native-blurhash'
|
||||
// preview, original, remote -> first show preview, then original, if original failed, then remote
|
||||
|
||||
export interface Props {
|
||||
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
|
||||
accessibilityHint?: AccessibilityProps['accessibilityHint']
|
||||
|
||||
hidden?: boolean
|
||||
uri: { preview?: string; original?: string; remote?: string }
|
||||
blurhash?: string
|
||||
@ -36,6 +40,8 @@ export interface Props {
|
||||
|
||||
const GracefullyImage = React.memo(
|
||||
({
|
||||
accessibilityLabel,
|
||||
accessibilityHint,
|
||||
hidden = false,
|
||||
uri,
|
||||
blurhash,
|
||||
@ -103,10 +109,7 @@ const GracefullyImage = React.memo(
|
||||
} else {
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.blurhash,
|
||||
{ backgroundColor: theme.disabled }
|
||||
]}
|
||||
style={[styles.blurhash, { backgroundColor: theme.disabled }]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -117,6 +120,11 @@ const GracefullyImage = React.memo(
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
{...(onPress
|
||||
? { accessibilityRole: 'imagebutton' }
|
||||
: { accessibilityRole: 'image' })}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityHint={accessibilityHint}
|
||||
style={[style, dimension, { backgroundColor: theme.shimmerDefault }]}
|
||||
{...(onPress
|
||||
? hidden
|
||||
|
@ -28,7 +28,11 @@ const ComponentHashtag: React.FC<Props> = ({
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Pressable style={styles.itemDefault} onPress={customOnPress || onPress}>
|
||||
<Pressable
|
||||
accessibilityRole='button'
|
||||
style={styles.itemDefault}
|
||||
onPress={customOnPress || onPress}
|
||||
>
|
||||
<Text style={[styles.itemHashtag, { color: theme.primaryDefault }]}>
|
||||
#{hashtag.name}
|
||||
</Text>
|
||||
|
@ -2,10 +2,20 @@ import Icon from '@components/Icon'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
||||
import {
|
||||
AccessibilityProps,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View
|
||||
} from 'react-native'
|
||||
import { Flow } from 'react-native-animated-spinkit'
|
||||
|
||||
export interface Props {
|
||||
accessibilityLabel?: string
|
||||
accessibilityHint?: string
|
||||
accessibilityState?: AccessibilityProps['accessibilityState']
|
||||
|
||||
type?: 'icon' | 'text'
|
||||
content: string
|
||||
native?: boolean
|
||||
@ -18,6 +28,11 @@ export interface Props {
|
||||
}
|
||||
|
||||
const HeaderRight: React.FC<Props> = ({
|
||||
// Accessibility - Start
|
||||
accessibilityLabel,
|
||||
accessibilityHint,
|
||||
accessibilityState,
|
||||
// Accessibility - End
|
||||
type = 'icon',
|
||||
content,
|
||||
native = true,
|
||||
@ -75,6 +90,10 @@ const HeaderRight: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityHint={accessibilityHint}
|
||||
accessibilityRole='button'
|
||||
accessibilityState={accessibilityState}
|
||||
onPress={onPress}
|
||||
children={children}
|
||||
disabled={disabled || loading}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React, { createElement } from 'react'
|
||||
import { StyleProp, View, ViewStyle } from 'react-native'
|
||||
import { AccessibilityProps, StyleProp, View, ViewStyle } from 'react-native'
|
||||
import * as FeatherIcon from 'react-native-feather'
|
||||
|
||||
export interface Props {
|
||||
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
|
||||
|
||||
name: string
|
||||
size: number
|
||||
color: string
|
||||
@ -12,6 +14,7 @@ export interface Props {
|
||||
}
|
||||
|
||||
const Icon: React.FC<Props> = ({
|
||||
accessibilityLabel,
|
||||
name,
|
||||
size,
|
||||
color,
|
||||
@ -21,6 +24,7 @@ const Icon: React.FC<Props> = ({
|
||||
}) => {
|
||||
return (
|
||||
<View
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
style={[
|
||||
style,
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Button from '@components/Button'
|
||||
import Icon from '@components/Icon'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { useAppsQuery } from '@utils/queryHooks/apps'
|
||||
import { useInstanceQuery } from '@utils/queryHooks/instance'
|
||||
import { getInstances } from '@utils/slices/instancesSlice'
|
||||
@ -7,7 +8,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import * as WebBrowser from 'expo-web-browser'
|
||||
import { debounce } from 'lodash'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import React, { RefObject, useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
Alert,
|
||||
@ -19,6 +20,7 @@ import {
|
||||
TextInput,
|
||||
View
|
||||
} from 'react-native'
|
||||
import { ScrollView } from 'react-native-gesture-handler'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { Placeholder } from 'rn-placeholder'
|
||||
import analytics from './analytics'
|
||||
@ -26,16 +28,19 @@ import InstanceAuth from './Instance/Auth'
|
||||
import InstanceInfo from './Instance/Info'
|
||||
|
||||
export interface Props {
|
||||
scrollViewRef?: RefObject<ScrollView>
|
||||
disableHeaderImage?: boolean
|
||||
goBack?: boolean
|
||||
}
|
||||
|
||||
const ComponentInstance: React.FC<Props> = ({
|
||||
scrollViewRef,
|
||||
disableHeaderImage,
|
||||
goBack = false
|
||||
}) => {
|
||||
const { t } = useTranslation('componentInstance')
|
||||
const { mode, theme } = useTheme()
|
||||
const { screenReaderEnabled } = useAccessibility()
|
||||
|
||||
const instances = useSelector(getInstances, () => true)
|
||||
const [domain, setDomain] = useState<string>()
|
||||
@ -139,6 +144,8 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
<View style={styles.base}>
|
||||
<View style={styles.inputRow}>
|
||||
<TextInput
|
||||
accessible={false}
|
||||
accessibilityRole='none'
|
||||
style={[
|
||||
styles.prefix,
|
||||
{
|
||||
@ -148,12 +155,8 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
}
|
||||
]}
|
||||
editable={false}
|
||||
children={
|
||||
<Text
|
||||
style={{ color: theme.primaryDefault }}
|
||||
children='https://'
|
||||
/>
|
||||
}
|
||||
placeholder='https://'
|
||||
placeholderTextColor={theme.primaryDefault}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
@ -172,10 +175,14 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
keyboardType='url'
|
||||
textContentType='URL'
|
||||
onSubmitEditing={onSubmitEditing}
|
||||
placeholder={t('server.textInput.placeholder')}
|
||||
placeholder={' ' + t('server.textInput.placeholder')}
|
||||
placeholderTextColor={theme.secondary}
|
||||
returnKeyType='go'
|
||||
keyboardAppearance={mode}
|
||||
{...(scrollViewRef && {
|
||||
onFocus: () =>
|
||||
setTimeout(() => scrollViewRef.current?.scrollToEnd(), 150)
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
type='text'
|
||||
@ -229,9 +236,21 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
color={theme.secondary}
|
||||
style={styles.disclaimerIcon}
|
||||
/>
|
||||
<Text style={[styles.disclaimerText, { color: theme.secondary }]}>
|
||||
<Text
|
||||
style={[styles.disclaimerText, { color: theme.secondary }]}
|
||||
accessibilityRole='link'
|
||||
onPress={() => {
|
||||
if (screenReaderEnabled) {
|
||||
analytics('view_privacy')
|
||||
WebBrowser.openBrowserAsync(
|
||||
'https://tooot.app/privacy-policy'
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('server.disclaimer.base')}
|
||||
<Text
|
||||
accessible
|
||||
style={{ color: theme.blue }}
|
||||
onPress={() => {
|
||||
analytics('view_privacy')
|
||||
@ -265,8 +284,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
prefix: {
|
||||
borderBottomWidth: 1,
|
||||
...StyleConstants.FontStyle.M,
|
||||
paddingRight: StyleConstants.Spacing.XS
|
||||
...StyleConstants.FontStyle.M
|
||||
},
|
||||
textInput: {
|
||||
flex: 1,
|
||||
|
@ -16,8 +16,10 @@ const InstanceInfo = React.memo(
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<View style={[styles.base, style]}>
|
||||
<Text style={[styles.header, { color: theme.primaryDefault }]}>{header}</Text>
|
||||
<View style={[styles.base, style]} accessible>
|
||||
<Text style={[styles.header, { color: theme.primaryDefault }]}>
|
||||
{header}
|
||||
</Text>
|
||||
{content ? (
|
||||
<Text style={[styles.content, { color: theme.primaryDefault }]}>
|
||||
{content}
|
||||
|
@ -7,7 +7,11 @@ export interface Props {
|
||||
}
|
||||
|
||||
const MenuContainer: React.FC<Props> = ({ children }) => {
|
||||
return <View style={styles.base}>{children}</View>
|
||||
return (
|
||||
<View style={styles.base}>
|
||||
{children}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Icon from '@components/Icon'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { ColorDefinitions } from '@utils/styles/themes'
|
||||
@ -41,6 +42,7 @@ const MenuRow: React.FC<Props> = ({
|
||||
onPress
|
||||
}) => {
|
||||
const { theme } = useTheme()
|
||||
const { screenReaderEnabled } = useAccessibility()
|
||||
|
||||
const loadingSpinkit = useMemo(
|
||||
() => (
|
||||
@ -55,11 +57,22 @@ const MenuRow: React.FC<Props> = ({
|
||||
)
|
||||
|
||||
return (
|
||||
<View style={styles.base}>
|
||||
<View
|
||||
style={styles.base}
|
||||
accessible
|
||||
accessibilityRole={switchValue ? 'switch' : 'button'}
|
||||
accessibilityState={switchValue ? { checked: switchValue } : undefined}
|
||||
>
|
||||
<TapGestureHandler
|
||||
onHandlerStateChange={({ nativeEvent }) =>
|
||||
nativeEvent.state === State.ACTIVE && !loading && onPress && onPress()
|
||||
}
|
||||
onHandlerStateChange={async ({ nativeEvent }) => {
|
||||
if (nativeEvent.state === State.ACTIVE && !loading) {
|
||||
if (screenReaderEnabled && switchOnValueChange) {
|
||||
switchOnValueChange()
|
||||
} else {
|
||||
if (onPress) onPress()
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<View style={styles.core}>
|
||||
<View style={styles.front}>
|
||||
|
@ -3,6 +3,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { getTheme } from '@utils/styles/themes'
|
||||
import React from 'react'
|
||||
import { AccessibilityInfo } from 'react-native'
|
||||
import FlashMessage, {
|
||||
hideMessage,
|
||||
showMessage
|
||||
@ -36,6 +37,8 @@ const displayMessage = ({
|
||||
mode: 'light' | 'dark'
|
||||
type: 'success' | 'error' | 'warning'
|
||||
}) => {
|
||||
AccessibilityInfo.announceForAccessibility(message + '.' + description)
|
||||
|
||||
enum iconMapping {
|
||||
success = 'CheckCircle',
|
||||
error = 'XCircle',
|
||||
@ -98,7 +101,10 @@ const Message = React.memo(
|
||||
...StyleConstants.FontStyle.M,
|
||||
fontWeight: StyleConstants.Font.Weight.Bold
|
||||
}}
|
||||
textStyle={{ color: theme.primaryDefault, ...StyleConstants.FontStyle.S }}
|
||||
textStyle={{
|
||||
color: theme.primaryDefault,
|
||||
...StyleConstants.FontStyle.S
|
||||
}}
|
||||
// @ts-ignore
|
||||
textProps={{ numberOfLines: 2 }}
|
||||
/>
|
||||
|
@ -4,6 +4,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
||||
import { adaptiveScale } from '@utils/styles/scaling'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { useSelector } from 'react-redux'
|
||||
@ -27,6 +28,7 @@ const ParseEmojis = React.memo(
|
||||
adaptiveSize = false,
|
||||
fontBold = false
|
||||
}: Props) => {
|
||||
const { t } = useTranslation('componentParse')
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
|
||||
const adaptiveFontsize = useSelector(getSettingsFontsize)
|
||||
@ -69,10 +71,10 @@ const ParseEmojis = React.memo(
|
||||
return emojiShortcode === `:${emoji.shortcode}:`
|
||||
})
|
||||
if (emojiIndex === -1) {
|
||||
return <Text key={emojiShortcode}>{emojiShortcode}</Text>
|
||||
return <Text key={emojiShortcode + i}>{emojiShortcode}</Text>
|
||||
} else {
|
||||
if (i === 0) {
|
||||
return <Text key={emojiShortcode}> </Text>
|
||||
return <Text key={emojiShortcode + i}> </Text>
|
||||
} else {
|
||||
const uri = reduceMotionEnabled
|
||||
? emojis[emojiIndex].static_url
|
||||
@ -80,7 +82,7 @@ const ParseEmojis = React.memo(
|
||||
if (validUrl.isHttpsUri(uri)) {
|
||||
return (
|
||||
<FastImage
|
||||
key={emojiShortcode}
|
||||
key={emojiShortcode + i}
|
||||
source={{ uri }}
|
||||
style={styles.image}
|
||||
/>
|
||||
@ -91,7 +93,7 @@ const ParseEmojis = React.memo(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return <Text key={str}>{str}</Text>
|
||||
return <Text key={i}>{str}</Text>
|
||||
}
|
||||
})
|
||||
) : (
|
||||
|
@ -53,6 +53,7 @@ const renderNode = ({
|
||||
: true
|
||||
return (
|
||||
<Text
|
||||
accessible
|
||||
key={index}
|
||||
style={{
|
||||
color: theme.blue,
|
||||
@ -251,6 +252,7 @@ const ParseHTML = React.memo(
|
||||
/>
|
||||
{expandAllow ? (
|
||||
<Pressable
|
||||
accessibilityLabel=''
|
||||
onPress={() => {
|
||||
analytics('status_readmore', { allow: expandAllow, expanded })
|
||||
layoutAnimation()
|
||||
|
@ -62,6 +62,7 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessible={highlighted ? false : true}
|
||||
style={[
|
||||
styles.statusView,
|
||||
{
|
||||
@ -84,6 +85,7 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
<TimelineAvatar
|
||||
queryKey={disableOnPress ? undefined : queryKey}
|
||||
account={actualStatus.account}
|
||||
highlighted={highlighted}
|
||||
/>
|
||||
<TimelineHeaderDefault
|
||||
queryKey={disableOnPress ? undefined : queryKey}
|
||||
|
@ -84,7 +84,11 @@ const TimelineNotifications: React.FC<Props> = ({
|
||||
}}
|
||||
>
|
||||
<View style={styles.header}>
|
||||
<TimelineAvatar queryKey={queryKey} account={actualAccount} />
|
||||
<TimelineAvatar
|
||||
queryKey={queryKey}
|
||||
account={actualAccount}
|
||||
highlighted={highlighted}
|
||||
/>
|
||||
<TimelineHeaderNotification
|
||||
queryKey={queryKey}
|
||||
notification={notification}
|
||||
|
@ -269,12 +269,28 @@ const TimelineActions: React.FC<Props> = ({
|
||||
>
|
||||
<View style={styles.actions}>
|
||||
<Pressable
|
||||
{...(highlighted
|
||||
? {
|
||||
accessibilityLabel: t(
|
||||
'shared.actions.reply.accessibilityLabel'
|
||||
),
|
||||
accessibilityRole: 'button'
|
||||
}
|
||||
: { accessibilityLabel: '' })}
|
||||
style={styles.action}
|
||||
onPress={onPressReply}
|
||||
children={childrenReply}
|
||||
/>
|
||||
|
||||
<Pressable
|
||||
{...(highlighted
|
||||
? {
|
||||
accessibilityLabel: t(
|
||||
'shared.actions.reblogged.accessibilityLabel'
|
||||
),
|
||||
accessibilityRole: 'button'
|
||||
}
|
||||
: { accessibilityLabel: '' })}
|
||||
style={styles.action}
|
||||
onPress={onPressReblog}
|
||||
children={childrenReblog}
|
||||
@ -284,12 +300,28 @@ const TimelineActions: React.FC<Props> = ({
|
||||
/>
|
||||
|
||||
<Pressable
|
||||
{...(highlighted
|
||||
? {
|
||||
accessibilityLabel: t(
|
||||
'shared.actions.favourited.accessibilityLabel'
|
||||
),
|
||||
accessibilityRole: 'button'
|
||||
}
|
||||
: { accessibilityLabel: '' })}
|
||||
style={styles.action}
|
||||
onPress={onPressFavourite}
|
||||
children={childrenFavourite}
|
||||
/>
|
||||
|
||||
<Pressable
|
||||
{...(highlighted
|
||||
? {
|
||||
accessibilityLabel: t(
|
||||
'shared.actions.bookmarked.accessibilityLabel'
|
||||
),
|
||||
accessibilityRole: 'button'
|
||||
}
|
||||
: { accessibilityLabel: '' })}
|
||||
style={styles.action}
|
||||
onPress={onPressBookmark}
|
||||
children={childrenBookmark}
|
||||
|
@ -28,6 +28,16 @@ const TimelineActionsUsers = React.memo(
|
||||
<View style={styles.base}>
|
||||
{status.reblogs_count > 0 ? (
|
||||
<Text
|
||||
accessibilityLabel={t(
|
||||
'shared.actionsUsers.reblogged_by.accessibilityLabel',
|
||||
{
|
||||
count: status.reblogs_count
|
||||
}
|
||||
)}
|
||||
accessibilityHint={t(
|
||||
'shared.actionsUsers.reblogged_by.accessibilityHint'
|
||||
)}
|
||||
accessibilityRole='button'
|
||||
style={[styles.text, { color: theme.secondary }]}
|
||||
onPress={() => {
|
||||
analytics('timeline_shared_actionsusers_press_boosted', {
|
||||
@ -41,13 +51,23 @@ const TimelineActionsUsers = React.memo(
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t('shared.actionsUsers.reblogged_by', {
|
||||
{t('shared.actionsUsers.reblogged_by.text', {
|
||||
count: status.reblogs_count
|
||||
})}
|
||||
</Text>
|
||||
) : null}
|
||||
{status.favourites_count > 0 ? (
|
||||
<Text
|
||||
accessibilityLabel={t(
|
||||
'shared.actionsUsers.favourited_by.accessibilityLabel',
|
||||
{
|
||||
count: status.reblogs_count
|
||||
}
|
||||
)}
|
||||
accessibilityHint={t(
|
||||
'shared.actionsUsers.favourited_by.accessibilityHint'
|
||||
)}
|
||||
accessibilityRole='button'
|
||||
style={[styles.text, { color: theme.secondary }]}
|
||||
onPress={() => {
|
||||
analytics('timeline_shared_actionsusers_press_boosted', {
|
||||
@ -61,7 +81,7 @@ const TimelineActionsUsers = React.memo(
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t('shared.actionsUsers.favourited_by', {
|
||||
{t('shared.actionsUsers.favourited_by.text', {
|
||||
count: status.favourites_count
|
||||
})}
|
||||
</Text>
|
||||
|
@ -52,6 +52,7 @@ const AttachmentAudio: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<View
|
||||
accessibilityLabel={audio.description}
|
||||
style={[
|
||||
styles.base,
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ const AttachmentImage = React.memo(
|
||||
return (
|
||||
<View style={styles.base}>
|
||||
<GracefullyImage
|
||||
accessibilityLabel={image.description}
|
||||
hidden={sensitiveShown}
|
||||
uri={{ original: image.preview_url, remote: image.remote_url }}
|
||||
blurhash={image.blurhash}
|
||||
|
@ -47,7 +47,11 @@ const AttachmentUnsupported: React.FC<Props> = ({
|
||||
<Text
|
||||
style={[
|
||||
styles.text,
|
||||
{ color: attachment.blurhash ? theme.backgroundDefault : theme.primaryDefault }
|
||||
{
|
||||
color: attachment.blurhash
|
||||
? theme.backgroundDefault
|
||||
: theme.primaryDefault
|
||||
}
|
||||
]}
|
||||
>
|
||||
{t('shared.attachment.unsupported.text')}
|
||||
@ -58,9 +62,9 @@ const AttachmentUnsupported: React.FC<Props> = ({
|
||||
content={t('shared.attachment.unsupported.button')}
|
||||
size='S'
|
||||
overlay
|
||||
onPress={async () => {
|
||||
onPress={() => {
|
||||
analytics('timeline_shared_attachment_unsupported_press')
|
||||
attachment.remote_url && (await openLink(attachment.remote_url))
|
||||
attachment.remote_url && openLink(attachment.remote_url)
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
@ -62,6 +62,7 @@ const AttachmentVideo: React.FC<Props> = ({
|
||||
]}
|
||||
>
|
||||
<Video
|
||||
accessibilityLabel={video.description}
|
||||
ref={videoPlayer}
|
||||
style={{
|
||||
width: '100%',
|
||||
|
@ -5,14 +5,17 @@ import { StackNavigationProp } from '@react-navigation/stack'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export interface Props {
|
||||
queryKey?: QueryKeyTimeline
|
||||
account: Mastodon.Account
|
||||
highlighted: boolean
|
||||
}
|
||||
|
||||
const TimelineAvatar = React.memo(
|
||||
({ queryKey, account }: Props) => {
|
||||
({ queryKey, account, highlighted }: Props) => {
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const navigation = useNavigation<
|
||||
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||
>()
|
||||
@ -26,6 +29,14 @@ const TimelineAvatar = React.memo(
|
||||
|
||||
return (
|
||||
<GracefullyImage
|
||||
{...(highlighted && {
|
||||
accessibilityLabel: t('shared.avatar.accessibilityLabel', {
|
||||
name: account.display_name
|
||||
}),
|
||||
accessibilityHint: t('shared.avatar.accessibilityHint', {
|
||||
name: account.display_name
|
||||
})
|
||||
})}
|
||||
onPress={onPress}
|
||||
uri={{ original: account.avatar_static }}
|
||||
dimension={{
|
||||
@ -35,8 +46,7 @@ const TimelineAvatar = React.memo(
|
||||
style={{
|
||||
borderRadius: StyleConstants.Avatar.M,
|
||||
overflow: 'hidden',
|
||||
marginRight: StyleConstants.Spacing.S,
|
||||
backgroundColor: 'red'
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
@ -18,6 +18,8 @@ const TimelineCard = React.memo(
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessible
|
||||
accessibilityRole='link'
|
||||
style={[styles.card, { borderColor: theme.border }]}
|
||||
onPress={async () => {
|
||||
analytics('timeline_shared_card_press')
|
||||
|
@ -4,6 +4,7 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Pressable, StyleSheet, View } from 'react-native'
|
||||
import HeaderSharedAccount from './HeaderShared/Account'
|
||||
import HeaderSharedApplication from './HeaderShared/Application'
|
||||
@ -19,6 +20,7 @@ export interface Props {
|
||||
|
||||
const TimelineHeaderDefault = React.memo(
|
||||
({ queryKey, rootQueryKey, status }: Props) => {
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const navigation = useNavigation()
|
||||
const { theme } = useTheme()
|
||||
|
||||
@ -36,6 +38,7 @@ const TimelineHeaderDefault = React.memo(
|
||||
|
||||
{queryKey ? (
|
||||
<Pressable
|
||||
accessibilityHint={t('shared.header.actions.accessibilityHint')}
|
||||
style={styles.action}
|
||||
onPress={() =>
|
||||
navigation.navigate('Screen-Actions', {
|
||||
|
@ -2,6 +2,7 @@ import { ParseEmojis } from '@root/components/Parse'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
@ -11,12 +12,19 @@ export interface Props {
|
||||
|
||||
const HeaderSharedAccount = React.memo(
|
||||
({ account, withoutName = false }: Props) => {
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<View style={styles.base}>
|
||||
{withoutName ? null : (
|
||||
<Text style={styles.name} numberOfLines={1}>
|
||||
<Text
|
||||
accessibilityHint={t(
|
||||
'shared.header.shared.account.name.accessibilityHint'
|
||||
)}
|
||||
style={styles.name}
|
||||
numberOfLines={1}
|
||||
>
|
||||
<ParseEmojis
|
||||
content={account.display_name || account.username}
|
||||
emojis={account.emojis}
|
||||
@ -25,6 +33,9 @@ const HeaderSharedAccount = React.memo(
|
||||
</Text>
|
||||
)}
|
||||
<Text
|
||||
accessibilityHint={t(
|
||||
'shared.header.shared.account.account.accessibilityHint'
|
||||
)}
|
||||
style={[styles.acct, { color: theme.secondary }]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
|
@ -17,6 +17,7 @@ const HeaderSharedApplication = React.memo(
|
||||
|
||||
return application && application.name !== 'Web' ? (
|
||||
<Text
|
||||
accessibilityRole='link'
|
||||
onPress={async () => {
|
||||
analytics('timeline_shared_header_application_press', {
|
||||
application
|
||||
|
@ -2,6 +2,7 @@ import Icon from '@components/Icon'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
@ -10,10 +11,12 @@ export interface Props {
|
||||
|
||||
const HeaderSharedMuted = React.memo(
|
||||
({ muted }: Props) => {
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { theme } = useTheme()
|
||||
|
||||
return muted ? (
|
||||
<Icon
|
||||
accessibilityLabel={t('shared.header.shared.muted.accessibilityLabel')}
|
||||
name='VolumeX'
|
||||
size={StyleConstants.Font.Size.S}
|
||||
color={theme.secondary}
|
||||
|
@ -2,6 +2,7 @@ import Icon from '@components/Icon'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
@ -10,12 +11,16 @@ export interface Props {
|
||||
|
||||
const HeaderSharedVisibility = React.memo(
|
||||
({ visibility }: Props) => {
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { theme } = useTheme()
|
||||
|
||||
switch (visibility) {
|
||||
case 'private':
|
||||
return (
|
||||
<Icon
|
||||
accessibilityLabel={t(
|
||||
'shared.header.shared.visibility.private.accessibilityLabel'
|
||||
)}
|
||||
name='Lock'
|
||||
size={StyleConstants.Font.Size.S}
|
||||
color={theme.secondary}
|
||||
@ -25,6 +30,9 @@ const HeaderSharedVisibility = React.memo(
|
||||
case 'direct':
|
||||
return (
|
||||
<Icon
|
||||
accessibilityLabel={t(
|
||||
'shared.header.shared.visibility.direct.accessibilityLabel'
|
||||
)}
|
||||
name='Mail'
|
||||
size={StyleConstants.Font.Size.S}
|
||||
color={theme.secondary}
|
||||
|
Reference in New Issue
Block a user