1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Using new text component

Need to use global accessibility checks rather than per text component which is not efficient
This commit is contained in:
Zhiyuan Zheng
2022-05-07 00:52:32 +02:00
parent 8caf315894
commit 7c48c61c99
60 changed files with 1302 additions and 1494 deletions

View File

@ -5,9 +5,10 @@ import { TabLocalStackParamList } from '@utils/navigation/navigators'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback } from 'react'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { Pressable, View } from 'react-native'
import analytics from './analytics'
import GracefullyImage from './GracefullyImage'
import CustomText from './Text'
export interface Props {
account: Mastodon.Account
@ -32,50 +33,45 @@ const ComponentAccount: React.FC<Props> = ({
return (
<Pressable
accessibilityRole='button'
style={[styles.itemDefault, styles.itemAccount]}
style={{
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
paddingVertical: StyleConstants.Spacing.M,
flexDirection: 'row',
alignItems: 'center'
}}
onPress={customOnPress || onPress}
>
<GracefullyImage
uri={{ original: account.avatar, static: account.avatar_static }}
style={styles.itemAccountAvatar}
style={{
alignSelf: 'flex-start',
width: StyleConstants.Avatar.S,
height: StyleConstants.Avatar.S,
borderRadius: 6,
marginRight: StyleConstants.Spacing.S
}}
/>
<View>
<Text numberOfLines={1}>
<CustomText numberOfLines={1}>
<ParseEmojis
content={account.display_name || account.username}
emojis={account.emojis}
size='S'
fontBold
/>
</Text>
<Text
</CustomText>
<CustomText
numberOfLines={1}
style={[styles.itemAccountAcct, { color: colors.secondary }]}
style={{
marginTop: StyleConstants.Spacing.XS,
color: colors.secondary
}}
>
@{account.acct}
</Text>
</CustomText>
</View>
</Pressable>
)
}
const styles = StyleSheet.create({
itemDefault: {
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
paddingVertical: StyleConstants.Spacing.M
},
itemAccount: {
flexDirection: 'row',
alignItems: 'center'
},
itemAccountAvatar: {
alignSelf: 'flex-start',
width: StyleConstants.Avatar.S,
height: StyleConstants.Avatar.S,
borderRadius: 6,
marginRight: StyleConstants.Spacing.S
},
itemAccountAcct: { marginTop: StyleConstants.Spacing.XS }
})
export default ComponentAccount

View File

@ -7,12 +7,11 @@ import {
AccessibilityProps,
Pressable,
StyleProp,
StyleSheet,
Text,
View,
ViewStyle
} from 'react-native'
import { Flow } from 'react-native-animated-spinkit'
import CustomText from './Text'
export interface Props {
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
@ -116,7 +115,7 @@ const Button: React.FC<Props> = ({
case 'text':
return (
<>
<Text
<CustomText
style={{
color: mainColor,
fontSize:
@ -146,8 +145,10 @@ const Button: React.FC<Props> = ({
busy: loading
}}
style={[
styles.button,
{
borderRadius: 100,
justifyContent: 'center',
alignItems: 'center',
borderWidth: overlay ? 0 : 1,
borderColor: mainColor,
backgroundColor: colorBackground,
@ -170,12 +171,4 @@ const Button: React.FC<Props> = ({
)
}
const styles = StyleSheet.create({
button: {
borderRadius: 100,
justifyContent: 'center',
alignItems: 'center'
}
})
export default Button

View File

@ -1,3 +1,4 @@
import CustomText from '@components/Text'
import { useAppDispatch } from '@root/store'
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
import { countInstanceEmoji } from '@utils/slices/instancesSlice'
@ -11,8 +12,6 @@ import {
findNodeHandle,
Pressable,
SectionList,
StyleSheet,
Text,
View
} from 'react-native'
import FastImage from 'react-native-fast-image'
@ -30,7 +29,12 @@ const EmojisList = React.memo(
const listHeader = useCallback(
({ section: { title } }) => (
<Text style={[styles.group, { color: colors.secondary }]}>{title}</Text>
<CustomText
fontStyle='S'
style={{ position: 'absolute', color: colors.secondary }}
>
{title}
</CustomText>
),
[]
)
@ -38,7 +42,15 @@ const EmojisList = React.memo(
const listItem = useCallback(
({ index, item }: { item: Mastodon.Emoji[]; index: number }) => {
return (
<View key={index} style={styles.emojis}>
<View
key={index}
style={{
flex: 1,
flexWrap: 'wrap',
marginTop: StyleConstants.Spacing.M,
marginRight: StyleConstants.Spacing.S
}}
>
{item.map(emoji => {
const uri = reduceMotionEnabled ? emoji.static_url : emoji.url
if (validUrl.isHttpsUri(uri)) {
@ -64,7 +76,12 @@ const EmojisList = React.memo(
'screenCompose:content.root.footer.emojis.accessibilityHint'
)}
source={{ uri }}
style={styles.emoji}
style={{
width: 32,
height: 32,
padding: StyleConstants.Spacing.S,
margin: StyleConstants.Spacing.S
}}
/>
</Pressable>
)
@ -104,23 +121,4 @@ const EmojisList = React.memo(
() => true
)
const styles = StyleSheet.create({
group: {
position: 'absolute',
...StyleConstants.FontStyle.S
},
emojis: {
flex: 1,
flexWrap: 'wrap',
marginTop: StyleConstants.Spacing.M,
marginRight: StyleConstants.Spacing.S
},
emoji: {
width: 32,
height: 32,
padding: StyleConstants.Spacing.S,
margin: StyleConstants.Spacing.S
}
})
export default EmojisList

View File

@ -4,8 +4,9 @@ import { TabLocalStackParamList } from '@utils/navigation/navigators'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback } from 'react'
import { Pressable, StyleSheet, Text } from 'react-native'
import { Pressable } from 'react-native'
import analytics from './analytics'
import CustomText from './Text'
export interface Props {
hashtag: Mastodon.Tag
@ -30,23 +31,14 @@ const ComponentHashtag: React.FC<Props> = ({
return (
<Pressable
accessibilityRole='button'
style={styles.itemDefault}
style={{ padding: StyleConstants.Spacing.S * 1.5 }}
onPress={customOnPress || onPress}
>
<Text style={[styles.itemHashtag, { color: colors.primaryDefault }]}>
<CustomText fontStyle='M' style={{ color: colors.primaryDefault }}>
#{hashtag.name}
</Text>
</CustomText>
</Pressable>
)
}
const styles = StyleSheet.create({
itemDefault: {
padding: StyleConstants.Spacing.S * 1.5
},
itemHashtag: {
...StyleConstants.FontStyle.M
}
})
export default ComponentHashtag

View File

@ -1,7 +1,7 @@
import CustomText from '@components/Text'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react'
import { StyleSheet, Text } from 'react-native'
export interface Props {
content: string
@ -14,11 +14,12 @@ const HeaderCenter = React.memo(
const { colors } = useTheme()
return (
<Text
style={[
styles.text,
{ color: inverted ? colors.primaryOverlay : colors.primaryDefault }
]}
<CustomText
style={{
fontSize: 18,
fontWeight: StyleConstants.Font.Weight.Bold,
color: inverted ? colors.primaryOverlay : colors.primaryDefault
}}
children={content}
/>
)
@ -26,11 +27,4 @@ const HeaderCenter = React.memo(
(prev, next) => prev.content === next.content
)
const styles = StyleSheet.create({
text: {
fontSize: 18,
fontWeight: StyleConstants.Font.Weight.Bold
}
})
export default HeaderCenter

View File

@ -1,8 +1,9 @@
import Icon from '@components/Icon'
import CustomText from '@components/Text'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useMemo } from 'react'
import { Pressable, StyleSheet, Text } from 'react-native'
import { Pressable } from 'react-native'
export interface Props {
type?: 'icon' | 'text'
@ -34,8 +35,9 @@ const HeaderLeft: React.FC<Props> = ({
)
case 'text':
return (
<Text
style={[styles.text, { color: colors.primaryDefault }]}
<CustomText
fontStyle='M'
style={{ color: colors.primaryDefault }}
children={content}
/>
)
@ -46,38 +48,27 @@ const HeaderLeft: React.FC<Props> = ({
<Pressable
onPress={onPress}
children={children}
style={[
styles.base,
{
backgroundColor: background
? colors.backgroundOverlayDefault
: undefined,
minHeight: 44,
minWidth: 44,
marginLeft: native
? -StyleConstants.Spacing.S
: StyleConstants.Spacing.S,
...(type === 'icon' && {
borderRadius: 100
}),
...(type === 'text' && {
paddingHorizontal: StyleConstants.Spacing.S
})
}
]}
style={{
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: background
? colors.backgroundOverlayDefault
: undefined,
minHeight: 44,
minWidth: 44,
marginLeft: native
? -StyleConstants.Spacing.S
: StyleConstants.Spacing.S,
...(type === 'icon' && {
borderRadius: 100
}),
...(type === 'text' && {
paddingHorizontal: StyleConstants.Spacing.S
})
}}
/>
)
}
const styles = StyleSheet.create({
base: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center'
},
text: {
...StyleConstants.FontStyle.M
}
})
export default HeaderLeft

View File

@ -1,14 +1,9 @@
import Icon from '@components/Icon'
import CustomText from '@components/Text'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useMemo } from 'react'
import {
AccessibilityProps,
Pressable,
StyleSheet,
Text,
View
} from 'react-native'
import { AccessibilityProps, Pressable, View } from 'react-native'
import { Flow } from 'react-native-animated-spinkit'
export interface Props {
@ -72,14 +67,12 @@ const HeaderRight: React.FC<Props> = ({
case 'text':
return (
<>
<Text
style={[
styles.text,
{
color: disabled ? colors.secondary : colors.primaryDefault,
opacity: loading ? 0 : 1
}
]}
<CustomText
fontStyle='M'
style={{
color: disabled ? colors.secondary : colors.primaryDefault,
opacity: loading ? 0 : 1
}}
children={content}
/>
{loading && loadingSpinkit}
@ -97,38 +90,27 @@ const HeaderRight: React.FC<Props> = ({
onPress={onPress}
children={children}
disabled={disabled || loading}
style={[
styles.base,
{
backgroundColor: background
? colors.backgroundOverlayDefault
: undefined,
minHeight: 44,
minWidth: 44,
marginRight: native
? -StyleConstants.Spacing.S
: StyleConstants.Spacing.S,
...(type === 'icon' && {
borderRadius: 100
}),
...(type === 'text' && {
paddingHorizontal: StyleConstants.Spacing.S
})
}
]}
style={{
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: background
? colors.backgroundOverlayDefault
: undefined,
minHeight: 44,
minWidth: 44,
marginRight: native
? -StyleConstants.Spacing.S
: StyleConstants.Spacing.S,
...(type === 'icon' && {
borderRadius: 100
}),
...(type === 'text' && {
paddingHorizontal: StyleConstants.Spacing.S
})
}}
/>
)
}
const styles = StyleSheet.create({
base: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center'
},
text: {
...StyleConstants.FontStyle.M
}
})
export default HeaderRight

View File

@ -9,17 +9,11 @@ import React, {
useRef,
useState
} from 'react'
import {
Platform,
StyleSheet,
Text,
TextInput,
TextInputProps,
View
} from 'react-native'
import { Platform, TextInput, TextInputProps, View } from 'react-native'
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
import { ComponentEmojis, EmojisButton, EmojisList } from './Emojis'
import EmojisContext from './Emojis/helpers/EmojisContext'
import CustomText from './Text'
export interface Props {
autoFocus?: boolean
@ -106,14 +100,14 @@ const Input: React.FC<Props> = ({
maxLength={options?.maxLength}
>
<View
style={[
styles.base,
{
borderColor: colors.border,
flexDirection: multiline ? 'column' : 'row',
alignItems: 'stretch'
}
]}
style={{
borderWidth: 1,
marginVertical: StyleConstants.Spacing.S,
padding: StyleConstants.Spacing.S,
borderColor: colors.border,
flexDirection: multiline ? 'column' : 'row',
alignItems: 'stretch'
}}
>
<EmojisContext.Consumer>
{({ emojisDispatch }) => (
@ -124,16 +118,15 @@ const Input: React.FC<Props> = ({
setInputFocused(false)
emojisDispatch({ type: 'activate', payload: false })
}}
style={[
styles.textInput,
{
color: colors.primaryDefault,
minHeight:
Platform.OS === 'ios' && multiline
? StyleConstants.Font.LineHeight.M * 5
: undefined
}
]}
style={{
flex: 1,
fontSize: StyleConstants.Font.Size.M,
color: colors.primaryDefault,
minHeight:
Platform.OS === 'ios' && multiline
? StyleConstants.Font.LineHeight.M * 5
: undefined
}}
onChangeText={setValue}
onSelectionChange={onSelectionChange}
value={value}
@ -149,16 +142,25 @@ const Input: React.FC<Props> = ({
</EmojisContext.Consumer>
{title ? (
<Animated.Text
style={[styles.title, animateTitle, { color: colors.secondary }]}
style={[
animateTitle,
{ position: 'absolute', color: colors.secondary }
]}
>
{title}
</Animated.Text>
) : null}
<View style={{ flexDirection: 'row', alignSelf: 'flex-end' }}>
{options?.maxLength && value?.length ? (
<Text style={[styles.maxLength, { color: colors.secondary }]}>
<CustomText
fontStyle='S'
style={{
paddingLeft: StyleConstants.Spacing.XS,
color: colors.secondary
}}
>
{value?.length} / {options.maxLength}
</Text>
</CustomText>
) : null}
{inputFocused ? <EmojisButton /> : null}
</View>
@ -168,24 +170,4 @@ const Input: React.FC<Props> = ({
)
}
const styles = StyleSheet.create({
base: {
alignItems: 'flex-end',
borderWidth: 1,
marginVertical: StyleConstants.Spacing.S,
padding: StyleConstants.Spacing.S
},
title: {
position: 'absolute'
},
textInput: {
flex: 1,
fontSize: StyleConstants.Font.Size.M
},
maxLength: {
...StyleConstants.FontStyle.S,
paddingLeft: StyleConstants.Spacing.XS
}
})
export default Input

View File

@ -15,8 +15,6 @@ import {
Image,
KeyboardAvoidingView,
Platform,
StyleSheet,
Text,
TextInput,
View
} from 'react-native'
@ -26,6 +24,7 @@ import { Placeholder } from 'rn-placeholder'
import analytics from './analytics'
import InstanceAuth from './Instance/Auth'
import InstanceInfo from './Instance/Info'
import CustomText from './Text'
export interface Props {
scrollViewRef?: RefObject<ScrollView>
@ -134,40 +133,50 @@ const ComponentInstance: React.FC<Props> = ({
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
{!disableHeaderImage ? (
<View style={styles.imageContainer}>
<View style={{ flexDirection: 'row' }}>
<Image
source={require('assets/images/welcome.png')}
style={styles.image}
style={{ resizeMode: 'contain', flex: 1, aspectRatio: 16 / 9 }}
/>
</View>
) : null}
<View style={styles.base}>
<View style={styles.inputRow}>
<View
style={{
marginTop: StyleConstants.Spacing.L,
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
}}
>
<View
style={{
flexDirection: 'row',
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
}}
>
<TextInput
accessible={false}
accessibilityRole='none'
style={[
styles.prefix,
{
color: colors.primaryDefault,
borderBottomColor: instanceQuery.isError
? colors.red
: colors.border
}
]}
style={{
borderBottomWidth: 1,
...StyleConstants.FontStyle.M,
color: colors.primaryDefault,
borderBottomColor: instanceQuery.isError
? colors.red
: colors.border
}}
editable={false}
defaultValue='https://'
/>
<TextInput
style={[
styles.textInput,
{
color: colors.primaryDefault,
borderBottomColor: instanceQuery.isError
? colors.red
: colors.border
}
]}
style={{
flex: 1,
borderBottomWidth: 1,
...StyleConstants.FontStyle.M,
marginRight: StyleConstants.Spacing.M,
color: colors.primaryDefault,
borderBottomColor: instanceQuery.isError
? colors.red
: colors.border
}}
onChangeText={onChangeText}
autoCapitalize='none'
clearButtonMode='never'
@ -205,9 +214,9 @@ const ComponentInstance: React.FC<Props> = ({
content={instanceQuery.data?.title || undefined}
potentialWidth={2}
/>
<View style={styles.instanceStats}>
<View style={{ flex: 1, flexDirection: 'row' }}>
<InstanceInfo
style={styles.stat1}
style={{ alignItems: 'flex-start' }}
header={t('server.information.accounts')}
content={
instanceQuery.data?.stats?.user_count?.toString() || undefined
@ -215,7 +224,7 @@ const ComponentInstance: React.FC<Props> = ({
potentialWidth={4}
/>
<InstanceInfo
style={styles.stat2}
style={{ alignItems: 'center' }}
header={t('server.information.statuses')}
content={
instanceQuery.data?.stats?.status_count?.toString() ||
@ -224,7 +233,7 @@ const ComponentInstance: React.FC<Props> = ({
potentialWidth={4}
/>
<InstanceInfo
style={styles.stat3}
style={{ alignItems: 'flex-end' }}
header={t('server.information.domains')}
content={
instanceQuery.data?.stats?.domain_count?.toString() ||
@ -234,15 +243,28 @@ const ComponentInstance: React.FC<Props> = ({
/>
</View>
</Placeholder>
<View style={styles.disclaimer}>
<View
style={{
flexDirection: 'row',
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
marginVertical: StyleConstants.Spacing.M
}}
>
<Icon
name='Lock'
size={StyleConstants.Font.Size.S}
color={colors.secondary}
style={styles.disclaimerIcon}
style={{
marginTop:
(StyleConstants.Font.LineHeight.S -
StyleConstants.Font.Size.S) /
2,
marginRight: StyleConstants.Spacing.XS
}}
/>
<Text
style={[styles.disclaimerText, { color: colors.secondary }]}
<CustomText
fontStyle='S'
style={{ flex: 1, color: colors.secondary }}
accessibilityRole='link'
onPress={() => {
if (screenReaderEnabled) {
@ -254,7 +276,7 @@ const ComponentInstance: React.FC<Props> = ({
}}
>
{t('server.disclaimer.base')}
<Text
<CustomText
accessible
style={{ color: colors.blue }}
onPress={() => {
@ -265,8 +287,8 @@ const ComponentInstance: React.FC<Props> = ({
}}
>
{t('server.disclaimer.privacy')}
</Text>
</Text>
</CustomText>
</CustomText>
</View>
</View>
</View>
@ -276,54 +298,4 @@ const ComponentInstance: React.FC<Props> = ({
)
}
const styles = StyleSheet.create({
imageContainer: { flexDirection: 'row' },
image: { resizeMode: 'contain', flex: 1, aspectRatio: 16 / 9 },
base: {
marginTop: StyleConstants.Spacing.L,
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
},
inputRow: {
flexDirection: 'row',
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
},
prefix: {
borderBottomWidth: 1,
...StyleConstants.FontStyle.M
},
textInput: {
flex: 1,
borderBottomWidth: 1,
...StyleConstants.FontStyle.M,
marginRight: StyleConstants.Spacing.M
},
instanceStats: {
flex: 1,
flexDirection: 'row'
},
stat1: {
alignItems: 'flex-start'
},
stat2: {
alignItems: 'center'
},
stat3: {
alignItems: 'flex-end'
},
disclaimer: {
flexDirection: 'row',
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
marginVertical: StyleConstants.Spacing.M
},
disclaimerIcon: {
marginTop:
(StyleConstants.Font.LineHeight.S - StyleConstants.Font.Size.S) / 2,
marginRight: StyleConstants.Spacing.XS
},
disclaimerText: {
flex: 1,
...StyleConstants.FontStyle.S
}
})
export default ComponentInstance

View File

@ -1,7 +1,8 @@
import CustomText from '@components/Text'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react'
import { StyleSheet, Text, View, ViewStyle } from 'react-native'
import { View, ViewStyle } from 'react-native'
import { PlaceholderLine } from 'rn-placeholder'
export interface Props {
@ -16,14 +17,33 @@ const InstanceInfo = React.memo(
const { colors } = useTheme()
return (
<View style={[styles.base, style]} accessible>
<Text style={[styles.header, { color: colors.primaryDefault }]}>
{header}
</Text>
<View
style={[
{
flex: 1,
marginTop: StyleConstants.Spacing.M,
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
paddingRight: StyleConstants.Spacing.Global.PagePadding
},
style
]}
accessible
>
<CustomText
fontStyle='S'
style={{
fontWeight: StyleConstants.Font.Weight.Bold,
marginBottom: StyleConstants.Spacing.XS,
color: colors.primaryDefault
}}
children={header}
/>
{content ? (
<Text style={[styles.content, { color: colors.primaryDefault }]}>
{content}
</Text>
<CustomText
fontStyle='M'
style={{ color: colors.primaryDefault }}
children={content}
/>
) : (
<PlaceholderLine
width={
@ -43,21 +63,4 @@ const InstanceInfo = React.memo(
(prev, next) => prev.content === next.content
)
const styles = StyleSheet.create({
base: {
flex: 1,
marginTop: StyleConstants.Spacing.M,
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
paddingRight: StyleConstants.Spacing.Global.PagePadding
},
header: {
...StyleConstants.FontStyle.S,
fontWeight: StyleConstants.Font.Weight.Bold,
marginBottom: StyleConstants.Spacing.XS
},
content: {
...StyleConstants.FontStyle.M
}
})
export default InstanceInfo

View File

@ -1,7 +1,8 @@
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import { View } from 'react-native'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import CustomText from '@components/Text'
export interface Props {
heading: string
@ -11,20 +12,16 @@ const MenuHeader: React.FC<Props> = ({ heading }) => {
const { colors } = useTheme()
return (
<View style={styles.base}>
<Text style={[styles.text, { color: colors.secondary }]}>{heading}</Text>
<View style={{ paddingBottom: StyleConstants.Spacing.S }}>
<CustomText
fontStyle='S'
fontWeight='Bold'
style={{ color: colors.secondary }}
>
{heading}
</CustomText>
</View>
)
}
const styles = StyleSheet.create({
base: {
paddingBottom: StyleConstants.Spacing.S
},
text: {
...StyleConstants.FontStyle.S,
fontWeight: StyleConstants.Font.Weight.Bold
}
})
export default MenuHeader

View File

@ -1,4 +1,5 @@
import Icon from '@components/Icon'
import CustomText from '@components/Text'
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
@ -99,12 +100,13 @@ const MenuRow: React.FC<Props> = ({
/>
) : null}
<View style={styles.main}>
<Text
style={[styles.title, { color: colors.primaryDefault }]}
<CustomText
fontStyle='M'
style={{ color: colors.primaryDefault }}
numberOfLines={1}
>
{title}
</Text>
</CustomText>
</View>
</View>
@ -112,18 +114,15 @@ const MenuRow: React.FC<Props> = ({
<View style={styles.back}>
{content ? (
typeof content === 'string' ? (
<Text
style={[
styles.content,
{
color: colors.secondary,
opacity: !iconBack && loading ? 0 : 1
}
]}
<CustomText
style={{
color: colors.secondary,
opacity: !iconBack && loading ? 0 : 1
}}
numberOfLines={1}
>
{content}
</Text>
</CustomText>
) : (
content
)
@ -150,9 +149,9 @@ const MenuRow: React.FC<Props> = ({
) : null}
</View>
{description ? (
<Text style={[styles.description, { color: colors.secondary }]}>
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
{description}
</Text>
</CustomText>
) : null}
</View>
</TapGestureHandler>
@ -187,9 +186,6 @@ const styles = StyleSheet.create({
main: {
flex: 1
},
title: {
...StyleConstants.FontStyle.M
},
description: {
...StyleConstants.FontStyle.S
},

View File

@ -1,10 +1,11 @@
import CustomText from '@components/Text'
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 { StyleSheet, Text } from 'react-native'
import { StyleSheet } from 'react-native'
import FastImage from 'react-native-fast-image'
import { useSelector } from 'react-redux'
import validUrl from 'valid-url'
@ -57,7 +58,7 @@ const ParseEmojis = React.memo(
}, [theme, adaptiveFontsize])
return (
<Text style={styles.text}>
<CustomText style={styles.text}>
{emojis ? (
content
.split(regexEmoji)
@ -69,30 +70,34 @@ const ParseEmojis = React.memo(
return emojiShortcode === `:${emoji.shortcode}:`
})
if (emojiIndex === -1) {
return <Text key={emojiShortcode + i}>{emojiShortcode}</Text>
return (
<CustomText key={emojiShortcode + i}>
{emojiShortcode}
</CustomText>
)
} else {
const uri = reduceMotionEnabled
? emojis[emojiIndex].static_url
: emojis[emojiIndex].url
if (validUrl.isHttpsUri(uri)) {
return (
<Text key={emojiShortcode + i}>
<CustomText key={emojiShortcode + i}>
{i === 0 ? ' ' : undefined}
<FastImage source={{ uri }} style={styles.image} />
</Text>
</CustomText>
)
} else {
return null
}
}
} else {
return <Text key={i}>{str}</Text>
return <CustomText key={i}>{str}</CustomText>
}
})
) : (
<Text>{content}</Text>
<CustomText>{content}</CustomText>
)}
</Text>
</CustomText>
)
},
(prev, next) => prev.content === next.content

View File

@ -2,6 +2,7 @@ import analytics from '@components/analytics'
import Icon from '@components/Icon'
import openLink from '@components/openLink'
import ParseEmojis from '@components/Parse/Emojis'
import CustomText from '@components/Text'
import { useNavigation, useRoute } from '@react-navigation/native'
import { StackNavigationProp } from '@react-navigation/stack'
import { TabLocalStackParamList } from '@utils/navigation/navigators'
@ -12,7 +13,7 @@ import { adaptiveScale } from '@utils/styles/scaling'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Pressable, Text, View } from 'react-native'
import { Pressable, View } from 'react-native'
import HTMLView from 'react-native-htmlview'
import { useSelector } from 'react-redux'
@ -53,7 +54,7 @@ const renderNode = ({
? routeParams.hashtag !== tag[1] && routeParams.hashtag !== tag[2]
: true
return (
<Text
<CustomText
accessible
key={index}
style={{
@ -72,7 +73,7 @@ const renderNode = ({
>
{node.children[0].data}
{node.children[1]?.children[0].data}
</Text>
</CustomText>
)
} else if (classes.includes('mention') && mentions) {
const accountIndex = mentions.findIndex(
@ -82,7 +83,7 @@ const renderNode = ({
? routeParams.account.id !== mentions[accountIndex]?.id
: true
return (
<Text
<CustomText
key={index}
style={{
color:
@ -102,7 +103,7 @@ const renderNode = ({
>
{node.children[0].data}
{node.children[1]?.children[0].data}
</Text>
</CustomText>
)
}
} else {
@ -113,7 +114,7 @@ const renderNode = ({
const shouldBeTag =
tags && tags.filter(tag => `#${tag.name}` === content).length > 0
return (
<Text
<CustomText
key={index}
style={{
color: colors.blue,
@ -142,7 +143,7 @@ const renderNode = ({
}}
/>
) : null}
</Text>
</CustomText>
)
}
break
@ -252,7 +253,7 @@ const ParseHTML = React.memo(
return (
<View style={{ overflow: 'hidden' }}>
<Text
<CustomText
children={children}
onTextLayout={onTextLayout}
numberOfLines={
@ -275,7 +276,7 @@ const ParseHTML = React.memo(
backgroundColor: colors.backgroundDefault
}}
>
<Text
<CustomText
style={{
textAlign: 'center',
...StyleConstants.FontStyle.S,

76
src/components/Text.tsx Normal file
View File

@ -0,0 +1,76 @@
import { StyleConstants } from '@utils/styles/constants'
import { useEffect, useState } from 'react'
import { AccessibilityInfo, Text, TextProps, TextStyle } from 'react-native'
type Props =
| {
style?: Omit<TextStyle, 'fontSize' | 'lineHeight' | 'fontWeight'>
fontStyle?: undefined
fontSize?: 'S' | 'M' | 'L'
lineHeight?: 'S' | 'M' | 'L'
fontWeight?: 'Normal' | 'Bold'
}
| {
style?: Omit<TextStyle, 'fontSize' | 'lineHeight' | 'fontWeight'>
fontStyle: 'S' | 'M' | 'L'
fontSize?: undefined
lineHeight?: undefined
fontWeight?: 'Normal' | 'Bold'
}
const CustomText: React.FC<Props & TextProps> = ({
children,
style,
fontStyle,
fontSize,
fontWeight = 'Normal',
lineHeight,
...rest
}) => {
const [isBoldText, setIsBoldText] = useState(false)
useEffect(() => {
const boldTextChangedSubscription = AccessibilityInfo.addEventListener(
'boldTextChanged',
boldTextChanged => {
setIsBoldText(boldTextChanged)
}
)
AccessibilityInfo.isBoldTextEnabled().then(boldTextEnabled => {
setIsBoldText(boldTextEnabled)
})
return () => {
boldTextChangedSubscription.remove()
}
}, [])
enum BoldMapping {
'Normal' = '600',
'Bold' = '800'
}
return (
<Text
style={[
style,
{ ...(fontStyle && StyleConstants.FontStyle[fontStyle]) },
{ ...(fontSize && { fontSize: StyleConstants.Font.Size[fontSize] }) },
{
...(lineHeight && {
lineHeight: StyleConstants.Font.LineHeight[lineHeight]
})
},
{
fontWeight: isBoldText
? BoldMapping[fontWeight]
: StyleConstants.Font.Weight[fontWeight]
}
]}
{...rest}
>
{children}
</Text>
)
}
export default CustomText

View File

@ -1,12 +1,13 @@
import analytics from '@components/analytics'
import Button from '@components/Button'
import Icon from '@components/Icon'
import CustomText from '@components/Text'
import { QueryKeyTimeline, useTimelineQuery } 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 { StyleSheet, Text, View } from 'react-native'
import { View } from 'react-native'
import { Circle } from 'react-native-animated-spinkit'
export interface Props {
@ -40,9 +41,16 @@ const TimelineEmpty = React.memo(
size={StyleConstants.Font.Size.L}
color={colors.primaryDefault}
/>
<Text style={[styles.error, { color: colors.primaryDefault }]}>
<CustomText
fontStyle='M'
style={{
marginTop: StyleConstants.Spacing.S,
marginBottom: StyleConstants.Spacing.L,
color: colors.primaryDefault
}}
>
{t('empty.error.message')}
</Text>
</CustomText>
<Button
type='text'
content={t('empty.error.button')}
@ -61,9 +69,16 @@ const TimelineEmpty = React.memo(
size={StyleConstants.Font.Size.L}
color={colors.primaryDefault}
/>
<Text style={[styles.error, { color: colors.primaryDefault }]}>
<CustomText
fontStyle='M'
style={{
marginTop: StyleConstants.Spacing.S,
marginBottom: StyleConstants.Spacing.L,
color: colors.primaryDefault
}}
>
{t('empty.success.message')}
</Text>
</CustomText>
</>
)
}
@ -85,12 +100,4 @@ const TimelineEmpty = React.memo(
() => true
)
const styles = StyleSheet.create({
error: {
...StyleConstants.FontStyle.M,
marginTop: StyleConstants.Spacing.S,
marginBottom: StyleConstants.Spacing.L
}
})
export default TimelineEmpty

View File

@ -1,10 +1,11 @@
import Icon from '@components/Icon'
import CustomText from '@components/Text'
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react'
import { Trans } from 'react-i18next'
import { Text, View } from 'react-native'
import { View } from 'react-native'
import { Circle } from 'react-native-animated-spinkit'
export interface Props {
@ -38,9 +39,7 @@ const TimelineFooter = React.memo(
{!disableInfinity && hasNextPage ? (
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
) : (
<Text
style={{ ...StyleConstants.FontStyle.S, color: colors.secondary }}
>
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
<Trans
i18nKey='componentTimeline:end.message'
components={[
@ -51,7 +50,7 @@ const TimelineFooter = React.memo(
/>
]}
/>
</Text>
</CustomText>
)}
</View>
)

View File

@ -1,8 +1,9 @@
import CustomText from '@components/Text'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { Text, View } from 'react-native'
import { View } from 'react-native'
const TimelineLookback = React.memo(
() => {
@ -19,14 +20,9 @@ const TimelineLookback = React.memo(
backgroundColor: colors.backgroundDefault
}}
>
<Text
style={{
...StyleConstants.FontStyle.S,
color: colors.primaryDefault
}}
>
<CustomText fontStyle='S' style={{ color: colors.primaryDefault }}>
{t('lookback.message')}
</Text>
</CustomText>
</View>
)
},

View File

@ -1,5 +1,6 @@
import haptics from '@components/haptics'
import Icon from '@components/Icon'
import CustomText from '@components/Text'
import {
QueryKeyTimeline,
TimelineData,
@ -9,7 +10,7 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { RefObject, useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { FlatList, Platform, StyleSheet, Text, View } from 'react-native'
import { FlatList, Platform, View } from 'react-native'
import { Circle } from 'react-native-animated-spinkit'
import Animated, {
Extrapolate,
@ -251,9 +252,18 @@ const TimelineRefresh: React.FC<Props> = ({
return (
<Animated.View style={headerPadding}>
<View style={styles.base}>
<View
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: CONTAINER_HEIGHT * 2,
alignItems: 'center'
}}
>
{isFetching ? (
<View style={styles.container2}>
<View style={{ height: CONTAINER_HEIGHT, justifyContent: 'center' }}>
<Circle
size={StyleConstants.Font.Size.L}
color={colors.secondary}
@ -261,9 +271,19 @@ const TimelineRefresh: React.FC<Props> = ({
</View>
) : (
<>
<View style={styles.container1}>
<Text
style={[styles.explanation, { color: colors.primaryDefault }]}
<View
style={{
flex: 1,
flexDirection: 'row',
height: CONTAINER_HEIGHT
}}
>
<CustomText
fontStyle='S'
style={{
lineHeight: CONTAINER_HEIGHT,
color: colors.primaryDefault
}}
onLayout={onLayout}
children={t('refresh.fetchPreviousPage')}
/>
@ -285,9 +305,15 @@ const TimelineRefresh: React.FC<Props> = ({
}
/>
</View>
<View style={styles.container2}>
<Text
style={[styles.explanation, { color: colors.primaryDefault }]}
<View
style={{ height: CONTAINER_HEIGHT, justifyContent: 'center' }}
>
<CustomText
fontStyle='S'
style={{
lineHeight: CONTAINER_HEIGHT,
color: colors.primaryDefault
}}
onLayout={onLayout}
children={t('refresh.refetch')}
/>
@ -299,25 +325,4 @@ const TimelineRefresh: React.FC<Props> = ({
)
}
const styles = StyleSheet.create({
base: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: CONTAINER_HEIGHT * 2,
alignItems: 'center'
},
container1: {
flex: 1,
flexDirection: 'row',
height: CONTAINER_HEIGHT
},
container2: { height: CONTAINER_HEIGHT, justifyContent: 'center' },
explanation: {
fontSize: StyleConstants.Font.Size.S,
lineHeight: CONTAINER_HEIGHT
}
})
export default TimelineRefresh

View File

@ -1,6 +1,7 @@
import analytics from '@components/analytics'
import Icon from '@components/Icon'
import { displayMessage } from '@components/Message'
import CustomText from '@components/Text'
import { useNavigation } from '@react-navigation/native'
import { StackNavigationProp } from '@react-navigation/stack'
import { RootStackParamList } from '@utils/navigation/navigators'
@ -13,7 +14,7 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { Pressable, StyleSheet, View } from 'react-native'
import { useQueryClient } from 'react-query'
export interface Props {
@ -185,7 +186,7 @@ const TimelineActions: React.FC<Props> = ({
size={StyleConstants.Font.Size.L}
/>
{status.replies_count > 0 ? (
<Text
<CustomText
style={{
color: colors.secondary,
fontSize: StyleConstants.Font.Size.M,
@ -193,7 +194,7 @@ const TimelineActions: React.FC<Props> = ({
}}
>
{status.replies_count}
</Text>
</CustomText>
) : null}
</>
),
@ -213,7 +214,7 @@ const TimelineActions: React.FC<Props> = ({
size={StyleConstants.Font.Size.L}
/>
{status.reblogs_count > 0 ? (
<Text
<CustomText
style={{
color: color(status.reblogged),
fontSize: StyleConstants.Font.Size.M,
@ -221,7 +222,7 @@ const TimelineActions: React.FC<Props> = ({
}}
>
{status.reblogs_count}
</Text>
</CustomText>
) : null}
</>
)
@ -236,7 +237,7 @@ const TimelineActions: React.FC<Props> = ({
size={StyleConstants.Font.Size.L}
/>
{status.favourites_count > 0 ? (
<Text
<CustomText
style={{
color: color(status.favourited),
fontSize: StyleConstants.Font.Size.M,
@ -245,7 +246,7 @@ const TimelineActions: React.FC<Props> = ({
}}
>
{status.favourites_count}
</Text>
</CustomText>
) : null}
</>
)
@ -269,7 +270,7 @@ const TimelineActions: React.FC<Props> = ({
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
}}
>
<View style={styles.actions}>
<View style={{ flexDirection: 'row' }}>
<Pressable
{...(highlighted
? {
@ -334,9 +335,6 @@ const TimelineActions: React.FC<Props> = ({
}
const styles = StyleSheet.create({
actions: {
flexDirection: 'row'
},
action: {
flex: 1,
flexDirection: 'row',

View File

@ -1,11 +1,12 @@
import analytics from '@components/analytics'
import Button from '@components/Button'
import openLink from '@components/openLink'
import CustomText from '@components/Text'
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'
import { View } from 'react-native'
import { Blurhash } from 'react-native-blurhash'
import attachmentAspectRatio from './aspectRatio'
@ -27,10 +28,14 @@ const AttachmentUnsupported: React.FC<Props> = ({
return (
<View
style={[
styles.base,
{ aspectRatio: attachmentAspectRatio({ total, index }) }
]}
style={{
flex: 1,
flexBasis: '50%',
padding: StyleConstants.Spacing.XS / 2,
justifyContent: 'center',
alignItems: 'center',
aspectRatio: attachmentAspectRatio({ total, index })
}}
>
{attachment.blurhash ? (
<Blurhash
@ -44,18 +49,18 @@ const AttachmentUnsupported: React.FC<Props> = ({
) : null}
{!sensitiveShown ? (
<>
<Text
style={[
styles.text,
{
color: attachment.blurhash
? colors.backgroundDefault
: colors.primaryDefault
}
]}
<CustomText
fontStyle='S'
style={{
textAlign: 'center',
marginBottom: StyleConstants.Spacing.S,
color: attachment.blurhash
? colors.backgroundDefault
: colors.primaryDefault
}}
>
{t('shared.attachment.unsupported.text')}
</Text>
</CustomText>
{attachment.remote_url ? (
<Button
type='text'
@ -74,19 +79,4 @@ const AttachmentUnsupported: React.FC<Props> = ({
)
}
const styles = StyleSheet.create({
base: {
flex: 1,
flexBasis: '50%',
padding: StyleConstants.Spacing.XS / 2,
justifyContent: 'center',
alignItems: 'center'
},
text: {
...StyleConstants.FontStyle.S,
textAlign: 'center',
marginBottom: StyleConstants.Spacing.S
}
})
export default AttachmentUnsupported

View File

@ -1,11 +1,12 @@
import analytics from '@components/analytics'
import GracefullyImage from '@components/GracefullyImage'
import openLink from '@components/openLink'
import CustomText from '@components/Text'
import { useNavigation } from '@react-navigation/native'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { Pressable, StyleSheet, View } from 'react-native'
export interface Props {
card: Pick<
@ -22,7 +23,16 @@ const TimelineCard = React.memo(({ card }: Props) => {
<Pressable
accessible
accessibilityRole='link'
style={[styles.card, { borderColor: colors.border }]}
style={{
flex: 1,
flexDirection: 'row',
height: StyleConstants.Font.LineHeight.M * 5,
marginTop: StyleConstants.Spacing.M,
borderWidth: StyleSheet.hairlineWidth,
borderRadius: 6,
overflow: 'hidden',
borderColor: colors.border
}}
onPress={async () => {
analytics('timeline_shared_card_press')
await openLink(card.url, navigation)
@ -33,71 +43,46 @@ const TimelineCard = React.memo(({ card }: Props) => {
<GracefullyImage
uri={{ original: card.image }}
blurhash={card.blurhash}
style={styles.left}
imageStyle={styles.image}
style={{ flexBasis: StyleConstants.Font.LineHeight.M * 5 }}
imageStyle={{ borderTopLeftRadius: 6, borderBottomLeftRadius: 6 }}
/>
) : null}
<View style={styles.right}>
<Text
<View style={{ flex: 1, padding: StyleConstants.Spacing.S }}>
<CustomText
fontStyle='S'
numberOfLines={2}
style={[styles.rightTitle, { color: colors.primaryDefault }]}
style={{
marginBottom: StyleConstants.Spacing.XS,
fontWeight: StyleConstants.Font.Weight.Bold,
color: colors.primaryDefault
}}
testID='title'
>
{card.title}
</Text>
</CustomText>
{card.description ? (
<Text
<CustomText
fontStyle='S'
numberOfLines={1}
style={[styles.rightDescription, { color: colors.primaryDefault }]}
style={{
marginBottom: StyleConstants.Spacing.XS,
color: colors.primaryDefault
}}
testID='description'
>
{card.description}
</Text>
</CustomText>
) : null}
<Text
<CustomText
fontStyle='S'
numberOfLines={1}
style={[styles.rightLink, { color: colors.secondary }]}
style={{ color: colors.secondary }}
>
{card.url}
</Text>
</CustomText>
</View>
</Pressable>
)
})
const styles = StyleSheet.create({
card: {
flex: 1,
flexDirection: 'row',
height: StyleConstants.Font.LineHeight.M * 5,
marginTop: StyleConstants.Spacing.M,
borderWidth: StyleSheet.hairlineWidth,
borderRadius: 6,
overflow: 'hidden'
},
left: {
flexBasis: StyleConstants.Font.LineHeight.M * 5
},
image: {
borderTopLeftRadius: 6,
borderBottomLeftRadius: 6
},
right: {
flex: 1,
padding: StyleConstants.Spacing.S
},
rightTitle: {
...StyleConstants.FontStyle.S,
marginBottom: StyleConstants.Spacing.XS,
fontWeight: StyleConstants.Font.Weight.Bold
},
rightDescription: {
...StyleConstants.FontStyle.S,
marginBottom: StyleConstants.Spacing.XS
},
rightLink: {
...StyleConstants.FontStyle.S
}
})
export default TimelineCard

View File

@ -1,4 +1,5 @@
import analytics from '@components/analytics'
import CustomText from '@components/Text'
import { useNavigation } from '@react-navigation/native'
import { StackNavigationProp } from '@react-navigation/stack'
import { TabLocalStackParamList } from '@utils/navigation/navigators'
@ -7,7 +8,7 @@ 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'
import { StyleSheet, View } from 'react-native'
export interface Props {
status: Pick<
@ -37,7 +38,7 @@ const TimelineFeedback = React.memo(
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<View style={{ flexDirection: 'row' }}>
{status.reblogs_count > 0 ? (
<Text
<CustomText
accessibilityLabel={t(
'shared.actionsUsers.reblogged_by.accessibilityLabel',
{
@ -64,10 +65,10 @@ const TimelineFeedback = React.memo(
{t('shared.actionsUsers.reblogged_by.text', {
count: status.reblogs_count
})}
</Text>
</CustomText>
) : null}
{status.favourites_count > 0 ? (
<Text
<CustomText
accessibilityLabel={t(
'shared.actionsUsers.favourited_by.accessibilityLabel',
{
@ -94,12 +95,12 @@ const TimelineFeedback = React.memo(
{t('shared.actionsUsers.favourited_by.text', {
count: status.favourites_count
})}
</Text>
</CustomText>
) : null}
</View>
<View>
{data && data.length > 1 ? (
<Text
<CustomText
accessibilityLabel={t(
'shared.actionsUsers.history.accessibilityLabel',
{
@ -121,7 +122,7 @@ const TimelineFeedback = React.memo(
{t('shared.actionsUsers.history.text', {
count: data.length - 1
})}
</Text>
</CustomText>
) : null}
</View>
</View>

View File

@ -1,3 +1,4 @@
import CustomText from '@components/Text'
import { store } from '@root/store'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import { getInstance, getInstanceAccount } from '@utils/slices/instancesSlice'
@ -6,7 +7,7 @@ import { useTheme } from '@utils/styles/ThemeManager'
import htmlparser2 from 'htmlparser2-without-node-native'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { Text, View } from 'react-native'
import { View } from 'react-native'
const TimelineFiltered = React.memo(
() => {
@ -15,9 +16,9 @@ const TimelineFiltered = React.memo(
return (
<View style={{ backgroundColor: colors.backgroundDefault }}>
<Text
<CustomText
fontStyle='S'
style={{
...StyleConstants.FontStyle.S,
color: colors.secondary,
textAlign: 'center',
paddingVertical: StyleConstants.Spacing.S,
@ -25,7 +26,7 @@ const TimelineFiltered = React.memo(
}}
>
{t('shared.filtered')}
</Text>
</CustomText>
</View>
)
},

View File

@ -1,9 +1,9 @@
import CustomText from '@components/Text'
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 { Text } from 'react-native'
export interface Props {
queryKey?: QueryKeyTimeline
@ -22,15 +22,15 @@ const TimelineFullConversation = React.memo(
status.mentions.filter(
mention => mention.id !== status.in_reply_to_account_id
).length) ? (
<Text
<CustomText
fontStyle='S'
style={{
...StyleConstants.FontStyle.S,
color: colors.blue,
marginTop: StyleConstants.Spacing.S
}}
>
{t('shared.fullConversation')}
</Text>
</CustomText>
) : null
},
() => true

View File

@ -2,6 +2,7 @@ import analytics from '@components/analytics'
import Icon from '@components/Icon'
import { displayMessage } from '@components/Message'
import { ParseEmojis } from '@components/Parse'
import CustomText from '@components/Text'
import {
QueryKeyTimeline,
useTimelineMutation
@ -10,7 +11,7 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Pressable, Text, View } from 'react-native'
import { Pressable, View } from 'react-native'
import { useQueryClient } from 'react-query'
import HeaderSharedCreated from './HeaderShared/Created'
import HeaderSharedMuted from './HeaderShared/Muted'
@ -20,22 +21,22 @@ const Names = ({ accounts }: { accounts: Mastodon.Account[] }) => {
const { colors } = useTheme()
return (
<Text
<CustomText
numberOfLines={1}
style={{ ...StyleConstants.FontStyle.M, color: colors.secondary }}
>
<Text>{t('shared.header.conversation.withAccounts')}</Text>
<CustomText>{t('shared.header.conversation.withAccounts')}</CustomText>
{accounts.map((account, index) => (
<Text key={account.id} numberOfLines={1}>
<CustomText key={account.id} numberOfLines={1}>
{index !== 0 ? t('common:separator') : undefined}
<ParseEmojis
content={account.display_name || account.username}
emojis={account.emojis}
fontBold
/>
</Text>
</CustomText>
))}
</Text>
</CustomText>
)
}

View File

@ -1,9 +1,10 @@
import CustomText from '@components/Text'
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'
import { View } from 'react-native'
export interface Props {
account: Mastodon.Account
@ -16,13 +17,13 @@ const HeaderSharedAccount = React.memo(
const { colors } = useTheme()
return (
<View style={styles.base}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
{withoutName ? null : (
<Text
<CustomText
accessibilityHint={t(
'shared.header.shared.account.name.accessibilityHint'
)}
style={styles.name}
style={{ marginRight: StyleConstants.Spacing.XS }}
numberOfLines={1}
>
<ParseEmojis
@ -30,34 +31,21 @@ const HeaderSharedAccount = React.memo(
emojis={account.emojis}
fontBold
/>
</Text>
</CustomText>
)}
<Text
<CustomText
accessibilityHint={t(
'shared.header.shared.account.account.accessibilityHint'
)}
style={[styles.acct, { color: colors.secondary }]}
style={{ flexShrink: 1, color: colors.secondary }}
numberOfLines={1}
>
@{account.acct}
</Text>
</CustomText>
</View>
)
},
() => true
)
const styles = StyleSheet.create({
base: {
flexDirection: 'row',
alignItems: 'center'
},
name: {
marginRight: StyleConstants.Spacing.XS
},
acct: {
flexShrink: 1
}
})
export default HeaderSharedAccount

View File

@ -1,10 +1,10 @@
import analytics from '@components/analytics'
import openLink from '@components/openLink'
import CustomText from '@components/Text'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet, Text } from 'react-native'
export interface Props {
application?: Mastodon.Application
@ -16,7 +16,8 @@ const HeaderSharedApplication = React.memo(
const { t } = useTranslation('componentTimeline')
return application && application.name !== 'Web' ? (
<Text
<CustomText
fontStyle='S'
accessibilityRole='link'
onPress={async () => {
analytics('timeline_shared_header_application_press', {
@ -24,24 +25,20 @@ const HeaderSharedApplication = React.memo(
})
application.website && (await openLink(application.website))
}}
style={[styles.application, { color: colors.secondary }]}
style={{
flex: 1,
marginLeft: StyleConstants.Spacing.S,
color: colors.secondary
}}
numberOfLines={1}
>
{t('shared.header.shared.application', {
application: application.name
})}
</Text>
</CustomText>
) : null
},
() => true
)
const styles = StyleSheet.create({
application: {
flex: 1,
...StyleConstants.FontStyle.S,
marginLeft: StyleConstants.Spacing.S
}
})
export default HeaderSharedApplication

View File

@ -1,13 +1,13 @@
import Icon from '@components/Icon'
import RelativeTime from '@components/RelativeTime'
import CustomText from '@components/Text'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { Text } from 'react-native'
export interface Props {
created_at: Mastodon.Status['created_at']
created_at: Mastodon.Status['created_at'] | number
edited_at?: Mastodon.Status['edited_at']
}
@ -18,11 +18,9 @@ const HeaderSharedCreated = React.memo(
return (
<>
<Text
style={{ ...StyleConstants.FontStyle.S, color: colors.secondary }}
>
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
<RelativeTime date={edited_at || created_at} />
</Text>
</CustomText>
{edited_at ? (
<Icon
accessibilityLabel={t(

View File

@ -5,6 +5,7 @@ import Icon from '@components/Icon'
import { displayMessage } from '@components/Message'
import { ParseEmojis } from '@components/Parse'
import RelativeTime from '@components/RelativeTime'
import CustomText from '@components/Text'
import {
MutationVarsTimelineUpdateStatusProperty,
QueryKeyTimeline,
@ -83,7 +84,7 @@ const TimelinePoll: React.FC<Props> = ({
if (!poll.expired) {
if (!sameAccount && !poll.voted) {
return (
<View style={styles.button}>
<View style={{ marginRight: StyleConstants.Spacing.S }}>
<Button
onPress={() => {
analytics('timeline_shared_vote_vote_press')
@ -110,7 +111,7 @@ const TimelinePoll: React.FC<Props> = ({
)
} else {
return (
<View style={styles.button}>
<View style={{ marginRight: StyleConstants.Spacing.S }}>
<Button
onPress={() => {
analytics('timeline_shared_vote_refresh_press')
@ -147,19 +148,19 @@ const TimelinePoll: React.FC<Props> = ({
const pollExpiration = useMemo(() => {
if (poll.expired) {
return (
<Text style={[styles.expiration, { color: colors.secondary }]}>
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
{t('shared.poll.meta.expiration.expired')}
</Text>
</CustomText>
)
} else {
if (poll.expires_at) {
return (
<Text style={[styles.expiration, { color: colors.secondary }]}>
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
<Trans
i18nKey='componentTimeline:shared.poll.meta.expiration.until'
components={[<RelativeTime date={poll.expires_at} />]}
/>
</Text>
</CustomText>
)
}
}
@ -179,10 +180,17 @@ const TimelinePoll: React.FC<Props> = ({
option => option.votes_count
)?.votes_count
return poll.options.map((option, index) => (
<View key={index} style={styles.optionContainer}>
<View style={styles.optionContent}>
<View
key={index}
style={{ flex: 1, paddingVertical: StyleConstants.Spacing.S }}
>
<View style={{ flex: 1, flexDirection: 'row' }}>
<Icon
style={styles.optionSelection}
style={{
paddingTop:
StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M,
marginRight: StyleConstants.Spacing.S
}}
name={
`${poll.own_votes?.includes(index) ? 'Check' : ''}${
poll.multiple ? 'Square' : 'Circle'
@ -193,11 +201,18 @@ const TimelinePoll: React.FC<Props> = ({
poll.own_votes?.includes(index) ? colors.blue : colors.disabled
}
/>
<Text style={styles.optionText}>
<CustomText style={{ flex: 1 }}>
<ParseEmojis content={option.title} emojis={poll.emojis} />
</Text>
<Text
style={[styles.optionPercentage, { color: colors.primaryDefault }]}
</CustomText>
<CustomText
fontStyle='M'
style={{
alignSelf: 'center',
marginLeft: StyleConstants.Spacing.S,
flexBasis: '20%',
textAlign: 'center',
color: colors.primaryDefault
}}
>
{poll.votes_count
? Math.round(
@ -207,21 +222,24 @@ const TimelinePoll: React.FC<Props> = ({
)
: 0}
%
</Text>
</CustomText>
</View>
<View
style={[
styles.background,
{
width: `${Math.round(
(option.votes_count / (poll.voters_count || poll.votes_count)) *
100
)}%`,
backgroundColor:
option.votes_count === maxValue ? colors.blue : colors.disabled
}
]}
style={{
height: StyleConstants.Spacing.XS,
minWidth: 2,
borderTopRightRadius: 10,
borderBottomRightRadius: 10,
marginTop: StyleConstants.Spacing.XS,
marginBottom: StyleConstants.Spacing.S,
width: `${Math.round(
(option.votes_count / (poll.voters_count || poll.votes_count)) *
100
)}%`,
backgroundColor:
option.votes_count === maxValue ? colors.blue : colors.disabled
}}
/>
</View>
))
@ -230,7 +248,7 @@ const TimelinePoll: React.FC<Props> = ({
return poll.options.map((option, index) => (
<Pressable
key={index}
style={styles.optionContainer}
style={{ flex: 1, paddingVertical: StyleConstants.Spacing.S }}
onPress={() => {
analytics('timeline_shared_vote_option_press')
!allOptions[index] && haptics('Light')
@ -253,16 +271,20 @@ const TimelinePoll: React.FC<Props> = ({
}
}}
>
<View style={[styles.optionContent]}>
<View style={{ flex: 1, flexDirection: 'row' }}>
<Icon
style={styles.optionSelection}
style={{
paddingTop:
StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M,
marginRight: StyleConstants.Spacing.S
}}
name={isSelected(index)}
size={StyleConstants.Font.Size.M}
color={colors.primaryDefault}
/>
<Text style={styles.optionText}>
<CustomText style={{ flex: 1 }}>
<ParseEmojis content={option.title} emojis={poll.emojis} />
</Text>
</CustomText>
</View>
</Pressable>
))
@ -271,25 +293,32 @@ const TimelinePoll: React.FC<Props> = ({
const pollVoteCounts = useMemo(() => {
if (poll.voters_count !== null) {
return (
<Text style={[styles.votes, { color: colors.secondary }]}>
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
{t('shared.poll.meta.count.voters', { count: poll.voters_count })}
{' • '}
</Text>
</CustomText>
)
} else if (poll.votes_count !== null) {
return (
<Text style={[styles.votes, { color: colors.secondary }]}>
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
{t('shared.poll.meta.count.votes', { count: poll.votes_count })}
{' • '}
</Text>
</CustomText>
)
}
}, [poll.voters_count, poll.votes_count])
return (
<View style={styles.base}>
<View style={{ marginTop: StyleConstants.Spacing.M }}>
{poll.expired || poll.voted ? pollBodyDisallow : pollBodyAllow}
<View style={styles.meta}>
<View
style={{
flex: 1,
flexDirection: 'row',
alignItems: 'center',
marginTop: StyleConstants.Spacing.XS
}}
>
{pollButton}
{pollVoteCounts}
{pollExpiration}
@ -298,55 +327,4 @@ const TimelinePoll: React.FC<Props> = ({
)
}
const styles = StyleSheet.create({
base: {
marginTop: StyleConstants.Spacing.M
},
optionContainer: {
flex: 1,
paddingVertical: StyleConstants.Spacing.S
},
optionContent: {
flex: 1,
flexDirection: 'row'
},
optionText: {
flex: 1
},
optionSelection: {
paddingTop: StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M,
marginRight: StyleConstants.Spacing.S
},
optionPercentage: {
...StyleConstants.FontStyle.M,
alignSelf: 'center',
marginLeft: StyleConstants.Spacing.S,
flexBasis: '20%',
textAlign: 'center'
},
background: {
height: StyleConstants.Spacing.XS,
minWidth: 2,
borderTopRightRadius: 10,
borderBottomRightRadius: 10,
marginTop: StyleConstants.Spacing.XS,
marginBottom: StyleConstants.Spacing.S
},
meta: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
marginTop: StyleConstants.Spacing.XS
},
button: {
marginRight: StyleConstants.Spacing.S
},
votes: {
...StyleConstants.FontStyle.S
},
expiration: {
...StyleConstants.FontStyle.S
}
})
export default TimelinePoll

View File

@ -1,5 +1,6 @@
import analytics from '@components/analytics'
import { ParseHTML } from '@components/Parse'
import CustomText from '@components/Text'
import { useTranslateQuery } from '@utils/queryHooks/translate'
import { getSettingsLanguage } from '@utils/slices/settingsSlice'
import { StyleConstants } from '@utils/styles/constants'
@ -7,7 +8,7 @@ import { useTheme } from '@utils/styles/ThemeManager'
import * as Localization from 'expo-localization'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Pressable, Text } from 'react-native'
import { Pressable } from 'react-native'
import { Circle } from 'react-native-animated-spinkit'
import { useSelector } from 'react-redux'
@ -85,9 +86,9 @@ const TimelineTranslate = React.memo(
}
}}
>
<Text
<CustomText
fontStyle='M'
style={{
...StyleConstants.FontStyle.M,
color:
isLoading || isSuccess
? colors.secondary
@ -106,14 +107,14 @@ const TimelineTranslate = React.memo(
source: data?.sourceLanguage
})
: t('shared.translate.default')}
</Text>
<Text>
</CustomText>
<CustomText>
{__DEV__
? ` Source: ${status.language}; Target: ${
Localization.locale || settingsLanguage || 'en'
}`
: undefined}
</Text>
</CustomText>
{isLoading ? (
<Circle
size={StyleConstants.Font.Size.M}