mirror of
https://github.com/tooot-app/app
synced 2025-01-30 02:04:55 +01:00
Fixed #118
This commit is contained in:
parent
8aa84f7568
commit
a023ad58f1
@ -4,7 +4,7 @@
|
||||
"native": "210511",
|
||||
"major": 2,
|
||||
"minor": 0,
|
||||
"patch": 1,
|
||||
"patch": 2,
|
||||
"expo": "41.0.0"
|
||||
},
|
||||
"description": "tooot app for Mastodon",
|
||||
|
9
src/@types/mastodon.d.ts
vendored
9
src/@types/mastodon.d.ts
vendored
@ -261,6 +261,15 @@ declare namespace Mastodon {
|
||||
verified_at: string | null
|
||||
}
|
||||
|
||||
type Filter = {
|
||||
id: string
|
||||
phrase: string
|
||||
context: ('home' | 'notifications' | 'public' | 'thread' | 'account')[]
|
||||
expires_at?: string
|
||||
irreversible: boolean
|
||||
whole_word: boolean
|
||||
}
|
||||
|
||||
type List = {
|
||||
id: string
|
||||
title: string
|
||||
|
@ -13,6 +13,7 @@ import pushUseReceive from '@utils/push/useReceive'
|
||||
import pushUseRespond from '@utils/push/useRespond'
|
||||
import { updatePreviousTab } from '@utils/slices/contextsSlice'
|
||||
import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences'
|
||||
import { updateFilters } from '@utils/slices/instances/updateFilters'
|
||||
import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { themes } from '@utils/styles/themes'
|
||||
@ -106,6 +107,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||
// Lazily update users's preferences, for e.g. composing default visibility
|
||||
useEffect(() => {
|
||||
if (instanceActive !== -1) {
|
||||
dispatch(updateFilters())
|
||||
dispatch(updateAccountPreferences())
|
||||
}
|
||||
}, [instanceActive])
|
||||
|
@ -10,14 +10,16 @@ import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { StackNavigationProp } from '@react-navigation/stack'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { getInstance, getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import htmlparser2 from 'htmlparser2-without-node-native'
|
||||
import { uniqBy } from 'lodash'
|
||||
import React, { useCallback } from 'react'
|
||||
import { Pressable, StyleSheet, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import TimelineActionsUsers from './Shared/ActionsUsers'
|
||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||
import TimelineFullConversation from './Shared/FullConversation'
|
||||
import TimelineTranslate from './Shared/Translate'
|
||||
|
||||
@ -49,6 +51,16 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
|
||||
let actualStatus = item.reblog ? item.reblog : item
|
||||
|
||||
const ownAccount = actualStatus.account.id === instanceAccount?.id
|
||||
|
||||
if (
|
||||
!highlighted &&
|
||||
queryKey &&
|
||||
shouldFilter({ status: actualStatus, queryKey })
|
||||
) {
|
||||
return <TimelineFiltered />
|
||||
}
|
||||
|
||||
const onPress = useCallback(() => {
|
||||
analytics('timeline_default_press', {
|
||||
page: queryKey ? queryKey[1].page : origin
|
||||
@ -118,7 +130,7 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
statusId={actualStatus.id}
|
||||
poll={actualStatus.poll}
|
||||
reblog={item.reblog ? true : false}
|
||||
sameAccount={actualStatus.account.id === instanceAccount?.id}
|
||||
sameAccount={ownAccount}
|
||||
/>
|
||||
) : null}
|
||||
{!disableDetails &&
|
||||
|
@ -17,6 +17,7 @@ import { uniqBy } from 'lodash'
|
||||
import React, { useCallback } from 'react'
|
||||
import { Pressable, StyleSheet, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||
import TimelineFullConversation from './Shared/FullConversation'
|
||||
|
||||
export interface Props {
|
||||
@ -30,6 +31,13 @@ const TimelineNotifications: React.FC<Props> = ({
|
||||
queryKey,
|
||||
highlighted = false
|
||||
}) => {
|
||||
if (
|
||||
notification.status &&
|
||||
shouldFilter({ status: notification.status, queryKey })
|
||||
) {
|
||||
return <TimelineFiltered />
|
||||
}
|
||||
|
||||
const { theme } = useTheme()
|
||||
const instanceAccount = useSelector(
|
||||
getInstanceAccount,
|
||||
@ -38,6 +46,7 @@ const TimelineNotifications: React.FC<Props> = ({
|
||||
const navigation = useNavigation<
|
||||
StackNavigationProp<Nav.TabLocalStackParamList>
|
||||
>()
|
||||
|
||||
const actualAccount = notification.status
|
||||
? notification.status.account
|
||||
: notification.account
|
||||
|
105
src/components/Timeline/Shared/Filtered.tsx
Normal file
105
src/components/Timeline/Shared/Filtered.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import { store } from '@root/store'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { getInstance, getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
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'
|
||||
|
||||
const TimelineFiltered = React.memo(
|
||||
() => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
return (
|
||||
<View style={{ backgroundColor: theme.backgroundDefault }}>
|
||||
<Text
|
||||
style={{
|
||||
...StyleConstants.FontStyle.S,
|
||||
color: theme.secondary,
|
||||
textAlign: 'center',
|
||||
paddingVertical: StyleConstants.Spacing.S,
|
||||
paddingLeft: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
{t('shared.filtered')}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
},
|
||||
() => true
|
||||
)
|
||||
|
||||
export const shouldFilter = ({
|
||||
status,
|
||||
queryKey
|
||||
}: {
|
||||
status: Mastodon.Status
|
||||
queryKey: QueryKeyTimeline
|
||||
}) => {
|
||||
const instance = getInstance(store.getState())
|
||||
const ownAccount =
|
||||
getInstanceAccount(store.getState())?.id === status.account.id
|
||||
|
||||
let shouldFilter = false
|
||||
if (queryKey && !ownAccount) {
|
||||
const parser = new htmlparser2.Parser({
|
||||
ontext (text: string) {
|
||||
const checkFilter = (filter: Mastodon.Filter) => {
|
||||
switch (filter.whole_word) {
|
||||
case true:
|
||||
if (new RegExp('\\b' + filter.phrase + '\\b').test(text)) {
|
||||
shouldFilter = true
|
||||
}
|
||||
break
|
||||
case false:
|
||||
if (new RegExp(filter.phrase).test(text)) {
|
||||
shouldFilter = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
instance?.filters.forEach(filter => {
|
||||
if (filter.expires_at) {
|
||||
if (new Date().getTime() > new Date(filter.expires_at).getTime()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch (queryKey[1].page) {
|
||||
case 'Following':
|
||||
case 'Local':
|
||||
case 'List':
|
||||
case 'Account_Default':
|
||||
if (filter.context.includes('home')) {
|
||||
checkFilter(filter)
|
||||
}
|
||||
break
|
||||
case 'Notifications':
|
||||
if (filter.context.includes('notifications')) {
|
||||
checkFilter(filter)
|
||||
}
|
||||
break
|
||||
case 'LocalPublic':
|
||||
if (filter.context.includes('public')) {
|
||||
checkFilter(filter)
|
||||
}
|
||||
break
|
||||
case 'Toot':
|
||||
if (filter.context.includes('thread')) {
|
||||
checkFilter(filter)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
parser.write(status.content)
|
||||
parser.end()
|
||||
}
|
||||
|
||||
return shouldFilter
|
||||
}
|
||||
|
||||
export default TimelineFiltered
|
@ -73,6 +73,7 @@
|
||||
"content": {
|
||||
"expandHint": "hidden content"
|
||||
},
|
||||
"filtered": "Filtered",
|
||||
"fullConversation": "Read conversations",
|
||||
"translate": {
|
||||
"default": "Translate",
|
||||
|
12
src/utils/slices/instances/updateFilters.ts
Normal file
12
src/utils/slices/instances/updateFilters.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import apiInstance from '@api/instance'
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||
|
||||
export const updateFilters = createAsyncThunk(
|
||||
'instances/updateFilters',
|
||||
async (): Promise<Mastodon.Filter[]> => {
|
||||
return apiInstance<Mastodon.Filter[]>({
|
||||
method: 'get',
|
||||
url: `filters`
|
||||
}).then(res => res.body)
|
||||
}
|
||||
)
|
@ -6,6 +6,7 @@ import { findIndex } from 'lodash'
|
||||
import addInstance from './instances/add'
|
||||
import removeInstance from './instances/remove'
|
||||
import { updateAccountPreferences } from './instances/updateAccountPreferences'
|
||||
import { updateFilters } from './instances/updateFilters'
|
||||
import { updateInstancePush } from './instances/updatePush'
|
||||
import { updateInstancePushAlert } from './instances/updatePushAlert'
|
||||
import { updateInstancePushDecode } from './instances/updatePushDecode'
|
||||
@ -29,6 +30,7 @@ export type Instance = {
|
||||
avatarStatic: Mastodon.Account['avatar_static']
|
||||
preferences: Mastodon.Preferences
|
||||
}
|
||||
filters: Mastodon.Filter[]
|
||||
notifications_filter: {
|
||||
follow: boolean
|
||||
favourite: boolean
|
||||
@ -236,6 +238,15 @@ const instancesSlice = createSlice({
|
||||
console.error(action.error)
|
||||
})
|
||||
|
||||
// Update Instance Account Filters
|
||||
.addCase(updateFilters.fulfilled, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
state.instances[activeIndex].filters = action.payload
|
||||
})
|
||||
.addCase(updateFilters.rejected, (_, action) => {
|
||||
console.error(action.error)
|
||||
})
|
||||
|
||||
// Update Instance Account Preferences
|
||||
.addCase(updateAccountPreferences.fulfilled, (state, action) => {
|
||||
const activeIndex = findInstanceActive(state.instances)
|
||||
|
Loading…
x
Reference in New Issue
Block a user