mirror of
https://github.com/tooot-app/app
synced 2025-01-12 17:34:16 +01:00
Fixed #525
HTML is removed. In this way, if a URL is changed, it can be highlighted as well
This commit is contained in:
parent
306bc45e33
commit
24ccee8afa
@ -1,6 +1,7 @@
|
||||
Enjoy toooting! This version includes following improvements and fixes:
|
||||
- Automatic setting detected language when tooting
|
||||
- Remember public timeline type selection
|
||||
- Show diffing of edit history
|
||||
- Added notification for admins
|
||||
- Fix whole word filter matching
|
||||
- Fix tablet cannot delete toot drafts
|
@ -1,6 +1,7 @@
|
||||
toooting愉快!此版本包括以下改进和修复:
|
||||
- 自动识别发嘟语言
|
||||
- 记住上次公共时间轴选项
|
||||
- 显示编辑历史的差异
|
||||
- 新增管理员推送通知
|
||||
- 修复过滤整词功能
|
||||
- 修复平板不能删除草稿
|
@ -43,6 +43,7 @@
|
||||
"@sharcoux/slider": "^6.1.1",
|
||||
"@tanstack/react-query": "^4.19.1",
|
||||
"axios": "^0.27.2",
|
||||
"diff": "^5.1.0",
|
||||
"expo": "^47.0.8",
|
||||
"expo-auth-session": "^3.7.3",
|
||||
"expo-av": "^13.0.2",
|
||||
@ -101,6 +102,7 @@
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@expo/config": "^7.0.3",
|
||||
"@types/diff": "^5.0.2",
|
||||
"@types/linkify-it": "^3.0.2",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/react": "~18.0.26",
|
||||
|
@ -5,7 +5,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 { Platform, StyleSheet } from 'react-native'
|
||||
import { Platform, StyleSheet, TextStyle } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { useSelector } from 'react-redux'
|
||||
import validUrl from 'valid-url'
|
||||
@ -18,10 +18,11 @@ export interface Props {
|
||||
size?: 'S' | 'M' | 'L'
|
||||
adaptiveSize?: boolean
|
||||
fontBold?: boolean
|
||||
style?: TextStyle
|
||||
}
|
||||
|
||||
const ParseEmojis = React.memo(
|
||||
({ content, emojis, size = 'M', adaptiveSize = false, fontBold = false }: Props) => {
|
||||
({ content, emojis, size = 'M', adaptiveSize = false, fontBold = false, style }: Props) => {
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
|
||||
const adaptiveFontsize = useSelector(getSettingsFontsize)
|
||||
@ -51,7 +52,7 @@ const ParseEmojis = React.memo(
|
||||
}, [theme, adaptiveFontsize])
|
||||
|
||||
return (
|
||||
<CustomText style={styles.text} fontWeight={fontBold ? 'Bold' : undefined}>
|
||||
<CustomText style={[styles.text, style]} fontWeight={fontBold ? 'Bold' : undefined}>
|
||||
{emojis ? (
|
||||
content
|
||||
.split(regexEmoji)
|
||||
|
18
src/helpers/removeHTML.ts
Normal file
18
src/helpers/removeHTML.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import htmlparser2 from 'htmlparser2-without-node-native'
|
||||
|
||||
const removeHTML = (text: string): string => {
|
||||
let raw: string = ''
|
||||
|
||||
const parser = new htmlparser2.Parser({
|
||||
ontext: (text: string) => {
|
||||
raw = raw + text
|
||||
}
|
||||
})
|
||||
|
||||
parser.write(text)
|
||||
parser.end()
|
||||
|
||||
return raw
|
||||
}
|
||||
|
||||
export default removeHTML
|
@ -4,44 +4,86 @@ import { ParseEmojis } from '@components/Parse'
|
||||
import ComponentSeparator from '@components/Separator'
|
||||
import CustomText from '@components/Text'
|
||||
import TimelineAttachment from '@components/Timeline/Shared/Attachment'
|
||||
import TimelineContent from '@components/Timeline/Shared/Content'
|
||||
import StatusContext from '@components/Timeline/Shared/Context'
|
||||
import HeaderSharedCreated from '@components/Timeline/Shared/HeaderShared/Created'
|
||||
import removeHTML from '@helpers/removeHTML'
|
||||
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||
import { useStatusHistory } from '@utils/queryHooks/statusesHistory'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { diffWords } from 'diff'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FlatList, View } from 'react-native'
|
||||
|
||||
const ContentView: React.FC<{ item: Mastodon.StatusHistory }> = ({ item }) => {
|
||||
const ContentView: React.FC<{
|
||||
item: Mastodon.StatusHistory
|
||||
prevItem?: Mastodon.StatusHistory
|
||||
}> = ({ item, prevItem }) => {
|
||||
const { colors } = useTheme()
|
||||
|
||||
const changesSpoiler = diffWords(
|
||||
removeHTML(prevItem?.spoiler_text || item.spoiler_text || ''),
|
||||
removeHTML(item.spoiler_text || '')
|
||||
)
|
||||
const changesContent = diffWords(
|
||||
removeHTML(prevItem?.content || item.content),
|
||||
removeHTML(item.content)
|
||||
)
|
||||
|
||||
return (
|
||||
// @ts-ignore
|
||||
<StatusContext.Provider value={{ status: item, disableOnPress: true }}>
|
||||
<HeaderSharedCreated created_at={item.created_at} />
|
||||
<TimelineContent />
|
||||
{item.poll?.options.map((option, index) => (
|
||||
<View key={index} style={{ flex: 1, paddingVertical: StyleConstants.Spacing.XS }}>
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<Icon
|
||||
<View style={{ padding: StyleConstants.Spacing.Global.PagePadding }}>
|
||||
<HeaderSharedCreated created_at={item.created_at} />
|
||||
{changesSpoiler.length && changesSpoiler[0].count ? (
|
||||
<CustomText fontSize='M' style={{ color: colors.primaryDefault }}>
|
||||
{changesSpoiler.map(({ value, added, removed }, index) => (
|
||||
<ParseEmojis
|
||||
key={index}
|
||||
content={value}
|
||||
emojis={item.emojis}
|
||||
style={{
|
||||
color: added ? colors.green : removed ? colors.red : undefined,
|
||||
textDecorationLine: removed ? 'line-through' : undefined
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</CustomText>
|
||||
) : null}
|
||||
<CustomText fontSize='M' style={{ color: colors.primaryDefault }}>
|
||||
{changesContent.map(({ value, added, removed }, index) => (
|
||||
<ParseEmojis
|
||||
key={index}
|
||||
content={value}
|
||||
emojis={item.emojis}
|
||||
style={{
|
||||
paddingTop: StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M,
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
color: added ? colors.green : removed ? colors.red : undefined,
|
||||
textDecorationLine: removed ? 'line-through' : undefined
|
||||
}}
|
||||
name='Circle'
|
||||
size={StyleConstants.Font.Size.M}
|
||||
color={colors.disabled}
|
||||
/>
|
||||
<CustomText style={{ flex: 1 }}>
|
||||
<ParseEmojis content={option.title} emojis={item.poll?.emojis} />
|
||||
</CustomText>
|
||||
))}
|
||||
</CustomText>
|
||||
{item.poll?.options.map((option, index) => (
|
||||
<View key={index} style={{ flex: 1, paddingVertical: StyleConstants.Spacing.XS }}>
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<Icon
|
||||
style={{
|
||||
paddingTop: StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M,
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
}}
|
||||
name='Circle'
|
||||
size={StyleConstants.Font.Size.M}
|
||||
color={colors.disabled}
|
||||
/>
|
||||
<CustomText style={{ flex: 1 }}>
|
||||
<ParseEmojis content={option.title} emojis={item.poll?.emojis} />
|
||||
</CustomText>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
<TimelineAttachment />
|
||||
))}
|
||||
<TimelineAttachment />
|
||||
</View>
|
||||
</StatusContext.Provider>
|
||||
)
|
||||
}
|
||||
@ -62,41 +104,18 @@ const TabSharedHistory: React.FC<TabSharedStackScreenProps<'Tab-Shared-History'>
|
||||
})
|
||||
}, [])
|
||||
|
||||
const dataReversed = data ? [...data].reverse() : []
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
style={{ flex: 1, minHeight: '100%', padding: StyleConstants.Spacing.Global.PagePadding }}
|
||||
data={
|
||||
data && data.length > 0
|
||||
? data
|
||||
.slice(0)
|
||||
.reverse()
|
||||
.filter((_, index) => index !== 0)
|
||||
: []
|
||||
}
|
||||
renderItem={({ item }) => <ContentView item={item} />}
|
||||
ItemSeparatorComponent={() => (
|
||||
<ComponentSeparator
|
||||
extraMarginLeft={0}
|
||||
style={{ marginVertical: StyleConstants.Spacing.Global.PagePadding }}
|
||||
/>
|
||||
style={{ flex: 1, minHeight: '100%' }}
|
||||
data={dataReversed}
|
||||
renderItem={({ item, index }) => (
|
||||
<ContentView item={item} prevItem={dataReversed[index + 1]} />
|
||||
)}
|
||||
ItemSeparatorComponent={ComponentSeparator}
|
||||
/>
|
||||
)
|
||||
|
||||
// return (
|
||||
// <ScrollView>
|
||||
// {data && data.length > 0
|
||||
// ? data
|
||||
// .slice(0)
|
||||
// .reverse()
|
||||
// .map((d, i) =>
|
||||
// i !== 0 ? (
|
||||
// <ContentView key={i} history={d} first={i === 1} last={i === data.length - 1} />
|
||||
// ) : null
|
||||
// )
|
||||
// : null}
|
||||
// </ScrollView>
|
||||
// )
|
||||
}
|
||||
|
||||
export default TabSharedHistory
|
||||
|
10
yarn.lock
10
yarn.lock
@ -2683,6 +2683,11 @@
|
||||
"@types/node" "*"
|
||||
"@types/responselike" "^1.0.0"
|
||||
|
||||
"@types/diff@^5.0.2":
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.0.2.tgz#dd565e0086ccf8bc6522c6ebafd8a3125c91c12b"
|
||||
integrity sha512-uw8eYMIReOwstQ0QKF0sICefSy8cNO/v7gOTiIy9SbwuHyEecJUm7qlgueOO5S1udZ5I/irVydHVwMchgzbKTg==
|
||||
|
||||
"@types/glob@^7.1.1":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
||||
@ -5256,6 +5261,11 @@ detect-port-alt@1.1.6:
|
||||
address "^1.0.1"
|
||||
debug "^2.6.0"
|
||||
|
||||
diff@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
|
||||
integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==
|
||||
|
||||
diffie-hellman@^5.0.0:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
|
||||
|
Loading…
Reference in New Issue
Block a user