Merge pull request #4848 from h3poteto/iss-4796/filter

refs #4796 Apply filters in timelines
This commit is contained in:
AkiraFukushima 2024-02-27 20:57:32 +09:00 committed by GitHub
commit 0e7713895f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 68 additions and 9 deletions

View File

@ -20,6 +20,8 @@
"hide_media": "Hide media",
"report": "Report {user}",
"open_original": "Open original page",
"filtered": "Filtered",
"show_anyway": "Show anyway",
"actions": {
"reply": "Reply",
"reblog": "reblog",

View File

@ -59,6 +59,7 @@ export default function Reply(props: Props) {
key={status.id}
onRefresh={() => {}}
openMedia={props.openMedia}
filters={[]}
/>
)}
/>

View File

@ -38,6 +38,7 @@ export default function Tag(props: Props) {
key={status.id}
onRefresh={() => {}}
openMedia={props.openMedia}
filters={[]}
/>
)}
/>

View File

@ -43,6 +43,7 @@ export default function Thread(props: Props) {
key={status.id}
onRefresh={() => {}}
openMedia={props.openMedia}
filters={[]}
/>
)}
/>

View File

@ -50,6 +50,7 @@ export default function Timeline(props: Props) {
key={index}
onRefresh={status => setStatuses(current => updateStatus(current, status))}
openMedia={props.openMedia}
filters={[]}
/>
))}
</>

View File

@ -27,6 +27,7 @@ export default function Notifications(props: Props) {
const [firstItemIndex, setFirstItemIndex] = useState(TIMELINE_MAX_STATUSES)
const [marker, setMarker] = useState<Marker | null>(null)
const [pleromaUnreads, setPleromaUnreads] = useState<Array<string>>([])
const [filters, setFilters] = useState<Array<Entity.Filter>>([])
const scrollerRef = useRef<HTMLElement | null>(null)
const streaming = useRef<WebSocketInterface | null>(null)
@ -37,6 +38,8 @@ export default function Notifications(props: Props) {
useEffect(() => {
const f = async () => {
const f = await loadFilter(props.client)
setFilters(f)
const res = await loadNotifications(props.client)
setNotifications(res)
const instance = await props.client.getInstance()
@ -80,6 +83,11 @@ export default function Notifications(props: Props) {
}
}, [marker, unreadNotifications, notifications])
const loadFilter = async (client: MegalodonInterface): Promise<Array<Entity.Filter>> => {
const res = await client.getFilters()
return res.data.filter(f => f.context.includes('notifications'))
}
const loadNotifications = async (client: MegalodonInterface, maxId?: string): Promise<Array<Entity.Notification>> => {
let options = { limit: 30 }
if (maxId) {
@ -209,6 +217,7 @@ export default function Notifications(props: Props) {
onRefresh={updateStatus}
key={notification.id}
openMedia={media => props.setAttachment(media)}
filters={filters}
/>
</div>
)

View File

@ -26,6 +26,7 @@ export default function Timeline(props: Props) {
const [firstItemIndex, setFirstItemIndex] = useState(TIMELINE_MAX_STATUSES)
const [composeHeight, setComposeHeight] = useState(120)
const [list, setList] = useState<Entity.List | null>(null)
const [filters, setFilters] = useState<Array<Entity.Filter>>([])
const router = useRouter()
const { formatMessage } = useIntl()
@ -50,6 +51,8 @@ export default function Timeline(props: Props) {
useEffect(() => {
const f = async () => {
const f = await loadFilter(props.timeline, props.client)
setFilters(f)
const res = await loadTimeline(props.timeline, props.client)
setStatuses(res)
const instance = await props.client.getInstance()
@ -106,6 +109,24 @@ export default function Timeline(props: Props) {
}
}, [props.timeline, props.client, props.account])
const loadFilter = async (tl: string, client: MegalodonInterface): Promise<Array<Entity.Filter>> => {
const res = await client.getFilters()
let context = 'home'
switch (tl) {
case 'home':
context = 'home'
break
case 'local':
case 'public':
context = 'public'
break
default:
context = 'home'
break
}
return res.data.filter(f => f.context.includes(context))
}
const loadTimeline = async (tl: string, client: MegalodonInterface, maxId?: string): Promise<Array<Entity.Status>> => {
let options = { limit: 30 }
if (maxId) {
@ -223,6 +244,7 @@ export default function Timeline(props: Props) {
key={status.id}
onRefresh={status => setStatuses(current => updateStatus(current, status))}
openMedia={media => props.setAttachment(media)}
filters={filters}
/>
)}
/>

View File

@ -10,6 +10,7 @@ type Props = {
client: MegalodonInterface
onRefresh: (status: Entity.Status) => void
openMedia: (media: Entity.Attachment) => void
filters: Array<Entity.Filter>
}
export default function Notification(props: Props) {
@ -23,6 +24,7 @@ export default function Notification(props: Props) {
status={props.notification.status}
onRefresh={props.onRefresh}
openMedia={props.openMedia}
filters={props.filters}
/>
)
} else {

View File

@ -19,11 +19,13 @@ type Props = {
client: MegalodonInterface
onRefresh: (status: Entity.Status) => void
openMedia: (media: Entity.Attachment) => void
filters: Array<Entity.Filter>
}
export default function Status(props: Props) {
const status = originalStatus(props.status)
const [spoilered, setSpoilered] = useState(status.spoiler_text.length > 0)
const [ignoreFilter, setIgnoreFilter] = useState(false)
const router = useRouter()
const { formatMessage } = useIntl()
@ -71,6 +73,20 @@ export default function Status(props: Props) {
}
}
if (
!ignoreFilter &&
props.filters.map(f => f.phrase).filter(keyword => props.status.content.toLowerCase().includes(keyword.toLowerCase())).length > 0
) {
return (
<div className="border-b mr-2 py-2 text-center">
<FormattedMessage id="timeline.status.filtered" />
<span className="text-blue-700 cursor-pointer pl-4" onClick={() => setIgnoreFilter(true)}>
<FormattedMessage id="timeline.status.show_anyway" />
</span>
</div>
)
}
return (
<div className="border-b mr-2 py-1">
{rebloggedHeader(

View File

@ -64,15 +64,19 @@ export const AccountsProvider: React.FC<Props> = ({ children }) => {
const client = generator(account.sns, account.url, account.access_token, 'Whalebird')
const instance = await client.getInstance()
const notifications = (await client.getNotifications()).data
const res = await client.getMarkers(['notifications'])
const marker = res.data as Entity.Marker
if (marker.notifications) {
const count = unreadCount(marker.notifications, notifications)
setUnreads(current =>
Object.assign({}, current, {
[account.id?.toString()]: count
})
)
try {
const res = await client.getMarkers(['notifications'])
const marker = res.data as Entity.Marker
if (marker.notifications) {
const count = unreadCount(marker.notifications, notifications)
setUnreads(current =>
Object.assign({}, current, {
[account.id?.toString()]: count
})
)
}
} catch (err) {
console.error(err)
}
const ws = generator(account.sns, instance.data.urls.streaming_api, account.access_token, 'Whalebird')