diff --git a/src/components/Parse/HTML.tsx b/src/components/Parse/HTML.tsx
index 03a4090d..4ff3436f 100644
--- a/src/components/Parse/HTML.tsx
+++ b/src/components/Parse/HTML.tsx
@@ -1,6 +1,7 @@
import Icon from '@components/Icon'
import openLink from '@components/openLink'
import ParseEmojis from '@components/Parse/Emojis'
+import StatusContext from '@components/Timeline/Shared/Context'
import { useNavigation, useRoute } from '@react-navigation/native'
import { StackNavigationProp } from '@react-navigation/stack'
import { TabLocalStackParamList } from '@utils/navigation/navigators'
@@ -11,43 +12,38 @@ import { adaptiveScale } from '@utils/styles/scaling'
import { useTheme } from '@utils/styles/ThemeManager'
import { ChildNode } from 'domhandler'
import { ElementType, parseDocument } from 'htmlparser2'
-import React, { useState } from 'react'
+import i18next from 'i18next'
+import React, { useContext, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Pressable, Text, TextStyleIOS, View } from 'react-native'
+import { Platform, Pressable, Text, TextStyleIOS, View } from 'react-native'
export interface Props {
content: string
size?: 'S' | 'M' | 'L'
- textStyles?: TextStyleIOS
adaptiveSize?: boolean
- emojis?: Mastodon.Emoji[]
- mentions?: Mastodon.Mention[]
- tags?: Mastodon.Tag[]
showFullLink?: boolean
numberOfLines?: number
expandHint?: string
- highlighted?: boolean
- disableDetails?: boolean
selectable?: boolean
setSpoilerExpanded?: React.Dispatch>
+ emojis?: Mastodon.Emoji[]
+ mentions?: Mastodon.Mention[]
}
const ParseHTML: React.FC = ({
content,
size = 'M',
- textStyles,
adaptiveSize = false,
- emojis,
- mentions,
- tags,
showFullLink = false,
numberOfLines = 10,
expandHint,
- highlighted = false,
- disableDetails = false,
selectable = false,
- setSpoilerExpanded
+ setSpoilerExpanded,
+ emojis,
+ mentions
}) => {
+ const { status, highlighted, disableDetails, excludeMentions } = useContext(StatusContext)
+
const [adaptiveFontsize] = useGlobalStorage.number('app.font_size')
const adaptedFontsize = adaptiveScale(
StyleConstants.Font.Size[size],
@@ -91,14 +87,16 @@ const ParseHTML: React.FC = ({
return ''
}
}
+ const startingOfText = useRef(false)
const renderNode = (node: ChildNode, index: number) => {
switch (node.type) {
case ElementType.Text:
+ node.data.trim().length && (startingOfText.current = true) // Removing empty spaces appeared between tags and mentions
return (
@@ -138,19 +136,28 @@ const ParseHTML: React.FC = ({
/>
)
}
- if (classes.includes('mention') && mentions?.length) {
- const mentionIndex = mentions.findIndex(mention => mention.url === href)
+ if (classes.includes('mention') && (status?.mentions?.length || mentions?.length)) {
+ const matchedMention = (status?.mentions || mentions || []).find(
+ mention => mention.url === href
+ )
+ if (
+ matchedMention &&
+ !startingOfText.current &&
+ excludeMentions?.current.find(eM => eM.id === matchedMention.id)
+ ) {
+ return null
+ }
const paramsAccount = (params as { account: Mastodon.Account } | undefined)?.account
- const sameAccount = paramsAccount?.id === mentions[mentionIndex]?.id
+ const sameAccount = paramsAccount?.id === matchedMention?.id
return (
-1 ? colors.blue : undefined }}
+ style={{ color: matchedMention ? colors.blue : undefined }}
onPress={() =>
- mentionIndex > -1 &&
+ matchedMention &&
!disableDetails &&
!sameAccount &&
- navigation.push('Tab-Shared-Account', { account: mentions[mentionIndex] })
+ navigation.push('Tab-Shared-Account', { account: matchedMention })
}
children={node.children.map(unwrapNode).join('')}
/>
@@ -159,7 +166,7 @@ const ParseHTML: React.FC = ({
}
const content = node.children.map(child => unwrapNode(child)).join('')
- const shouldBeTag = tags && tags.find(tag => `#${tag.name}` === content)
+ const shouldBeTag = status?.tags?.find(tag => `#${tag.name}` === content)
return (
= ({
style={{
fontSize: adaptedFontsize,
lineHeight: adaptedLineheight,
- ...textStyles,
+ ...(Platform.OS === 'ios' &&
+ status?.language &&
+ i18next.dir(status.language) === 'rtl' &&
+ ({ writingDirection: 'rtl' } as { writingDirection: 'rtl' })),
height: numberOfLines === 1 && !expanded ? 0 : undefined
}}
numberOfLines={
diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx
index 94a5b81d..26c8da51 100644
--- a/src/components/Timeline/Default.tsx
+++ b/src/components/Timeline/Default.tsx
@@ -51,7 +51,7 @@ const TimelineDefault: React.FC = ({
}) => {
const status = item.reblog ? item.reblog : item
const rawContent = useRef([])
- if (highlighted) {
+ if (highlighted || isConversation) {
rawContent.current = [
removeHTML(status.content),
status.spoiler_text ? removeHTML(status.spoiler_text) : ''
@@ -72,6 +72,7 @@ const TimelineDefault: React.FC = ({
? !preferences?.['reading:expand:spoilers'] && !spoilerExpanded
: false
const detectedLanguage = useRef(status.language || '')
+ const excludeMentions = useRef([])
const mainStyle: StyleProp = {
flex: 1,
@@ -169,6 +170,7 @@ const TimelineDefault: React.FC = ({
spoilerHidden,
rawContent,
detectedLanguage,
+ excludeMentions,
highlighted,
inThread: queryKey?.[1].page === 'Toot',
disableDetails,
diff --git a/src/components/Timeline/Shared/Content.tsx b/src/components/Timeline/Shared/Content.tsx
index c6003378..13e2f135 100644
--- a/src/components/Timeline/Shared/Content.tsx
+++ b/src/components/Timeline/Shared/Content.tsx
@@ -3,10 +3,9 @@ import CustomText from '@components/Text'
import { usePreferencesQuery } from '@utils/queryHooks/preferences'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
-import i18next from 'i18next'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
-import { Platform, View } from 'react-native'
+import { View } from 'react-native'
import StatusContext from './Context'
export interface Props {
@@ -23,11 +22,6 @@ const TimelineContent: React.FC = ({ notificationOwnToot = false, setSpoi
const { data: preferences } = usePreferencesQuery()
- const isRTLiOSTextStyles =
- Platform.OS === 'ios' && status.language && i18next.dir(status.language) === 'rtl'
- ? ({ writingDirection: 'rtl' } as { writingDirection: 'rtl' })
- : undefined
-
return (
{status.spoiler_text?.length ? (
@@ -36,13 +30,7 @@ const TimelineContent: React.FC = ({ notificationOwnToot = false, setSpoi
content={status.spoiler_text}
size={highlighted ? 'L' : 'M'}
adaptiveSize
- emojis={status.emojis}
- mentions={status.mentions}
- tags={status.tags}
numberOfLines={999}
- highlighted={highlighted}
- disableDetails={disableDetails}
- textStyles={isRTLiOSTextStyles}
/>
{inThread ? (
= ({ notificationOwnToot = false, setSpoi
content={status.content}
size={highlighted ? 'L' : 'M'}
adaptiveSize
- emojis={status.emojis}
- mentions={status.mentions}
- tags={status.tags}
numberOfLines={
preferences?.['reading:expand:spoilers'] || inThread
? notificationOwnToot
@@ -72,9 +57,6 @@ const TimelineContent: React.FC = ({ notificationOwnToot = false, setSpoi
}
expandHint={t('shared.content.expandHint')}
setSpoilerExpanded={setSpoilerExpanded}
- highlighted={highlighted}
- disableDetails={disableDetails}
- textStyles={isRTLiOSTextStyles}
/>
>
) : (
@@ -82,12 +64,7 @@ const TimelineContent: React.FC = ({ notificationOwnToot = false, setSpoi
content={status.content}
size={highlighted ? 'L' : 'M'}
adaptiveSize
- emojis={status.emojis}
- mentions={status.mentions}
- tags={status.tags}
numberOfLines={highlighted || inThread ? 999 : notificationOwnToot ? 2 : undefined}
- disableDetails={disableDetails}
- textStyles={isRTLiOSTextStyles}
/>
)}
diff --git a/src/components/Timeline/Shared/Context.tsx b/src/components/Timeline/Shared/Context.tsx
index 8917c2fb..80f1079e 100644
--- a/src/components/Timeline/Shared/Context.tsx
+++ b/src/components/Timeline/Shared/Context.tsx
@@ -14,6 +14,7 @@ type StatusContextType = {
spoilerHidden?: boolean
rawContent?: React.MutableRefObject // When highlighted, for translate, edit history
detectedLanguage?: React.MutableRefObject
+ excludeMentions?: React.MutableRefObject
highlighted?: boolean
inThread?: boolean
diff --git a/src/components/Timeline/Shared/HeaderDefault.tsx b/src/components/Timeline/Shared/HeaderDefault.tsx
index 7fa0545d..c96bf9ef 100644
--- a/src/components/Timeline/Shared/HeaderDefault.tsx
+++ b/src/components/Timeline/Shared/HeaderDefault.tsx
@@ -13,6 +13,7 @@ import HeaderSharedAccount from './HeaderShared/Account'
import HeaderSharedApplication from './HeaderShared/Application'
import HeaderSharedCreated from './HeaderShared/Created'
import HeaderSharedMuted from './HeaderShared/Muted'
+import HeaderSharedReplies from './HeaderShared/Replies'
import HeaderSharedVisibility from './HeaderShared/Visibility'
const TimelineHeaderDefault: React.FC = () => {
@@ -64,6 +65,7 @@ const TimelineHeaderDefault: React.FC = () => {
/>
+
diff --git a/src/components/Timeline/Shared/HeaderShared/Replies.tsx b/src/components/Timeline/Shared/HeaderShared/Replies.tsx
new file mode 100644
index 00000000..cd7d975e
--- /dev/null
+++ b/src/components/Timeline/Shared/HeaderShared/Replies.tsx
@@ -0,0 +1,50 @@
+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, { Fragment, useContext } from 'react'
+import { useTranslation } from 'react-i18next'
+import StatusContext from '../Context'
+
+const HeaderSharedReplies: React.FC = () => {
+ const { status, rawContent, excludeMentions, isConversation } = useContext(StatusContext)
+ if (!isConversation) return null
+
+ const navigation = useNavigation()
+ const { t } = useTranslation('componentTimeline')
+ const { colors } = useTheme()
+
+ const mentionsBeginning = rawContent?.current?.[0]
+ .match(new RegExp(/^(?:@\S+\s+)+/))?.[0]
+ ?.match(new RegExp(/@\S+/, 'g'))
+ excludeMentions &&
+ (excludeMentions.current =
+ mentionsBeginning?.length && status?.mentions
+ ? status.mentions.filter(mention => mentionsBeginning.includes(`@${mention.username}`))
+ : [])
+
+ return excludeMentions?.current.length ? (
+
+ Replies
+ {excludeMentions.current.map((mention, index) => (
+
+ {' '}
+ navigation.push('Tab-Shared-Account', { account: mention })}
+ />
+
+ ))}
+
+ ) : null
+}
+
+export default HeaderSharedReplies
diff --git a/src/utils/helpers/removeHTML.ts b/src/utils/helpers/removeHTML.ts
index 7f1b9832..069e9ae1 100644
--- a/src/utils/helpers/removeHTML.ts
+++ b/src/utils/helpers/removeHTML.ts
@@ -15,7 +15,7 @@ const removeHTML = (text: string): string => {
parser.write(text)
parser.end()
- return raw
+ return raw.replace(new RegExp(/\s$/), '')
}
export default removeHTML