This commit is contained in:
xmflsct 2022-12-31 12:56:10 +01:00
parent f0734e52e7
commit 65e9f41a3b
6 changed files with 93 additions and 78 deletions

View File

@ -15,7 +15,7 @@ import { ElementType, parseDocument } from 'htmlparser2'
import i18next from 'i18next' import i18next from 'i18next'
import React, { useContext, useRef, useState } from 'react' import React, { useContext, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Platform, Pressable, Text, TextStyleIOS, View } from 'react-native' import { Platform, Pressable, Text, View } from 'react-native'
export interface Props { export interface Props {
content: string content: string
@ -78,8 +78,8 @@ const ParseHTML: React.FC<Props> = ({
return node.data return node.data
case ElementType.Tag: case ElementType.Tag:
if (node.name === 'span') { if (node.name === 'span') {
if (node.attribs.class?.includes('invisible')) return '' if (node.attribs.class?.includes('invisible') && !showFullLink) return ''
if (node.attribs.class?.includes('ellipsis')) if (node.attribs.class?.includes('ellipsis') && !showFullLink)
return node.children.map(child => unwrapNode(child)).join('') + '...' return node.children.map(child => unwrapNode(child)).join('') + '...'
} }
return node.children.map(child => unwrapNode(child)).join('') return node.children.map(child => unwrapNode(child)).join('')
@ -87,15 +87,26 @@ const ParseHTML: React.FC<Props> = ({
return '' return ''
} }
} }
const startingOfText = useRef<boolean>(false) const openingMentions = useRef<boolean>(true)
const renderNode = (node: ChildNode, index: number) => { const renderNode = (node: ChildNode, index: number) => {
switch (node.type) { switch (node.type) {
case ElementType.Text: case ElementType.Text:
node.data.trim().length && (startingOfText.current = true) // Removing empty spaces appeared between tags and mentions let content: string = node.data
if (openingMentions.current) {
if (node.data.trim().length) {
openingMentions.current = false // Removing empty spaces appeared between tags and mentions
content = excludeMentions?.current.length
? node.data.replace(new RegExp(/^\s+/), '')
: node.data
} else {
content = node.data.trim()
}
}
return ( return (
<ParseEmojis <ParseEmojis
key={index} key={index}
content={node.data.replace(new RegExp(/^\s+/), '')} content={content}
emojis={status?.emojis || emojis} emojis={status?.emojis || emojis}
size={size} size={size}
adaptiveSize={adaptiveSize} adaptiveSize={adaptiveSize}
@ -108,6 +119,7 @@ const ParseHTML: React.FC<Props> = ({
const href = node.attribs.href const href = node.attribs.href
if (classes) { if (classes) {
if (classes.includes('hashtag')) { if (classes.includes('hashtag')) {
openingMentions.current = false
const tag = href.match(new RegExp(/\/tags?\/(.*)/, 'i'))?.[1].toLowerCase() const tag = href.match(new RegExp(/\/tags?\/(.*)/, 'i'))?.[1].toLowerCase()
const paramsHashtag = (params as { hashtag: Mastodon.Tag['name'] } | undefined) const paramsHashtag = (params as { hashtag: Mastodon.Tag['name'] } | undefined)
?.hashtag ?.hashtag
@ -142,7 +154,6 @@ const ParseHTML: React.FC<Props> = ({
) )
if ( if (
matchedMention && matchedMention &&
!startingOfText.current &&
excludeMentions?.current.find(eM => eM.id === matchedMention.id) excludeMentions?.current.find(eM => eM.id === matchedMention.id)
) { ) {
return null return null
@ -165,6 +176,7 @@ const ParseHTML: React.FC<Props> = ({
} }
} }
openingMentions.current = false
const content = node.children.map(child => unwrapNode(child)).join('') const content = node.children.map(child => unwrapNode(child)).join('')
const shouldBeTag = status?.tags?.find(tag => `#${tag.name}` === content) const shouldBeTag = status?.tags?.find(tag => `#${tag.name}` === content)
return ( return (
@ -182,7 +194,7 @@ const ParseHTML: React.FC<Props> = ({
} }
} }
}} }}
children={content !== href ? content : showFullLink ? href : content} children={content}
/> />
) )
break break

View File

@ -3,7 +3,7 @@ import { useNavigation } from '@react-navigation/native'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React, { Fragment, useContext } from 'react' import React, { Fragment, useContext } from 'react'
import { useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
import StatusContext from '../Context' import StatusContext from '../Context'
const HeaderSharedReplies: React.FC = () => { const HeaderSharedReplies: React.FC = () => {
@ -11,7 +11,7 @@ const HeaderSharedReplies: React.FC = () => {
if (!isConversation) return null if (!isConversation) return null
const navigation = useNavigation<any>() const navigation = useNavigation<any>()
const { t } = useTranslation('componentTimeline') const { t } = useTranslation(['common', 'componentTimeline'])
const { colors } = useTheme() const { colors } = useTheme()
const mentionsBeginning = rawContent?.current?.[0] const mentionsBeginning = rawContent?.current?.[0]
@ -26,25 +26,27 @@ const HeaderSharedReplies: React.FC = () => {
return excludeMentions?.current.length ? ( return excludeMentions?.current.length ? (
<CustomText <CustomText
fontStyle='S' fontStyle='S'
style={{ style={{ flex: 1, marginLeft: StyleConstants.Spacing.S, color: colors.secondary }}
marginLeft: StyleConstants.Spacing.S, numberOfLines={1}
flexDirection: 'row',
color: colors.secondary
}}
> >
<> <Trans
{t('shared.header.shared.replies')} ns='componentTimeline'
{excludeMentions.current.map((mention, index) => ( i18nKey='shared.header.shared.replies'
<Fragment key={index}> components={[
{' '} <>
<CustomText {excludeMentions.current.map((mention, index) => (
style={{ color: colors.blue, paddingLeft: StyleConstants.Spacing.S }} <Fragment key={index}>
children={`@${mention.username}`} {index > 0 ? t('common:separator') : null}
onPress={() => navigation.push('Tab-Shared-Account', { account: mention })} <CustomText
/> style={{ color: colors.blue, paddingLeft: StyleConstants.Spacing.S }}
</Fragment> children={`@${mention.username}`}
))} onPress={() => navigation.push('Tab-Shared-Account', { account: mention })}
</> />
</Fragment>
))}
</>
]}
/>
</CustomText> </CustomText>
) : null ) : null
} }

View File

@ -122,7 +122,7 @@
"muted": { "muted": {
"accessibilityLabel": "Toot muted" "accessibilityLabel": "Toot muted"
}, },
"replies": "Replies", "replies": "Replies <0 />",
"visibility": { "visibility": {
"direct": { "direct": {
"accessibilityLabel": "Toot is a direct message" "accessibilityLabel": "Toot is a direct message"

View File

@ -83,7 +83,10 @@ const Root: React.FC<NativeStackScreenProps<TabPublicStackParamList, 'Tab-Public
swipeEnabled swipeEnabled
renderScene={renderScene} renderScene={renderScene}
renderTabBar={() => null} renderTabBar={() => null}
onIndexChange={index => setSegment(index)} onIndexChange={index => {
setSegment(index)
setGlobalStorage('app.prev_public_segment', segments[index])
}}
navigationState={{ index: segment, routes }} navigationState={{ index: segment, routes }}
initialLayout={{ width: Dimensions.get('window').width }} initialLayout={{ width: Dimensions.get('window').width }}
/> />

View File

@ -1,4 +1,3 @@
import Icon from '@components/Icon'
import { ParseHTML } from '@components/Parse' import { ParseHTML } from '@components/Parse'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
@ -18,10 +17,39 @@ const AccountInformationFields: React.FC<Props> = ({ account, myInfo }) => {
const { colors } = useTheme() const { colors } = useTheme()
return ( return (
<View style={[styles.fields, { borderTopColor: colors.border }]}> <View
style={{
borderTopWidth: StyleSheet.hairlineWidth,
marginBottom: StyleConstants.Spacing.M,
borderTopColor: colors.border
}}
>
{account.fields.map((field, index) => ( {account.fields.map((field, index) => (
<View key={index} style={[styles.field, { borderBottomColor: colors.border }]}> <View
<View style={[styles.fieldLeft, { borderRightColor: colors.border }]}> key={index}
style={[
{
flex: 1,
flexDirection: 'row',
borderBottomWidth: StyleSheet.hairlineWidth,
paddingTop: StyleConstants.Spacing.S,
paddingBottom: StyleConstants.Spacing.S,
borderBottomColor: colors.border
},
field.verified_at ? { backgroundColor: 'rgba(0, 255, 0, 0.035)' } : undefined
]}
>
<View
style={{
flex: 1,
flexDirection: 'row',
alignItems: 'center',
borderRightWidth: 1,
paddingLeft: StyleConstants.Spacing.S,
paddingRight: StyleConstants.Spacing.S,
borderRightColor: colors.border
}}
>
<ParseHTML <ParseHTML
content={field.name} content={field.name}
size={'S'} size={'S'}
@ -30,16 +58,15 @@ const AccountInformationFields: React.FC<Props> = ({ account, myInfo }) => {
numberOfLines={5} numberOfLines={5}
selectable selectable
/> />
{field.verified_at ? (
<Icon
name='CheckCircle'
size={StyleConstants.Font.Size.M}
color={colors.primaryDefault}
style={styles.fieldCheck}
/>
) : null}
</View> </View>
<View style={styles.fieldRight}> <View
style={{
flex: 2,
justifyContent: 'center',
paddingLeft: StyleConstants.Spacing.S,
paddingRight: StyleConstants.Spacing.S
}}
>
<ParseHTML <ParseHTML
content={field.value} content={field.value}
size={'S'} size={'S'}
@ -55,33 +82,4 @@ const AccountInformationFields: React.FC<Props> = ({ account, myInfo }) => {
) )
} }
const styles = StyleSheet.create({
fields: {
borderTopWidth: StyleSheet.hairlineWidth,
marginBottom: StyleConstants.Spacing.M
},
field: {
flex: 1,
flexDirection: 'row',
borderBottomWidth: StyleSheet.hairlineWidth,
paddingTop: StyleConstants.Spacing.S,
paddingBottom: StyleConstants.Spacing.S
},
fieldLeft: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
borderRightWidth: 1,
paddingLeft: StyleConstants.Spacing.S,
paddingRight: StyleConstants.Spacing.S
},
fieldCheck: { marginLeft: StyleConstants.Spacing.XS },
fieldRight: {
flex: 2,
justifyContent: 'center',
paddingLeft: StyleConstants.Spacing.S,
paddingRight: StyleConstants.Spacing.S
}
})
export default AccountInformationFields export default AccountInformationFields

View File

@ -65,6 +65,8 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
}) })
const heights = useRef<(number | undefined)[]>([]) const heights = useRef<(number | undefined)[]>([])
const MAX_LEVEL = 10
const ARC = StyleConstants.Avatar.XS / 4
return ( return (
<FlatList <FlatList
@ -73,9 +75,6 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
windowSize={7} windowSize={7}
data={data?.body} data={data?.body}
renderItem={({ item, index }) => { renderItem={({ item, index }) => {
const MAX_LEVEL = 10
const ARC = StyleConstants.Avatar.XS / 4
const prev = data?.body[index - 1]?._level || 0 const prev = data?.body[index - 1]?._level || 0
const curr = item._level const curr = item._level
const next = data?.body[index + 1]?._level || 0 const next = data?.body[index + 1]?._level || 0
@ -85,7 +84,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
style={{ style={{
paddingLeft: paddingLeft:
index > (data?.highlightIndex || 0) index > (data?.highlightIndex || 0)
? Math.min(item._level, MAX_LEVEL) * StyleConstants.Spacing.S ? Math.min(item._level - 1, MAX_LEVEL) * StyleConstants.Spacing.S
: undefined : undefined
}} }}
onLayout={({ onLayout={({
@ -206,11 +205,12 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
<> <>
<ComponentSeparator <ComponentSeparator
extraMarginLeft={ extraMarginLeft={
toot.id === leadingItem.id leadingItem.id === toot.id
? 0 ? 0
: StyleConstants.Avatar.XS + : StyleConstants.Avatar.XS +
StyleConstants.Spacing.S + StyleConstants.Spacing.S +
Math.max(0, leadingItem._level - 1) * 8 Math.min(Math.max(0, leadingItem._level - 1), MAX_LEVEL) *
StyleConstants.Spacing.S
} }
/> />
{leadingItem._level > 1 {leadingItem._level > 1