From e3e21152a9173c3aa99c5fc9a9b5c2edc813b3fa Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Tue, 27 Feb 2024 20:49:27 +0900 Subject: [PATCH] refs #4796 Apply filters in timelines --- locales/en/translation.json | 2 ++ renderer/components/detail/Reply.tsx | 1 + renderer/components/detail/Tag.tsx | 1 + renderer/components/detail/Thread.tsx | 1 + .../components/detail/profile/Timeline.tsx | 1 + .../components/timelines/Notifications.tsx | 9 ++++++++ renderer/components/timelines/Timeline.tsx | 22 +++++++++++++++++++ .../timelines/notification/Notification.tsx | 2 ++ .../components/timelines/status/Status.tsx | 16 ++++++++++++++ renderer/provider/accounts.tsx | 22 +++++++++++-------- 10 files changed, 68 insertions(+), 9 deletions(-) diff --git a/locales/en/translation.json b/locales/en/translation.json index 33bcc882..098bbbad 100644 --- a/locales/en/translation.json +++ b/locales/en/translation.json @@ -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", diff --git a/renderer/components/detail/Reply.tsx b/renderer/components/detail/Reply.tsx index afd0abc4..3abe7ab9 100644 --- a/renderer/components/detail/Reply.tsx +++ b/renderer/components/detail/Reply.tsx @@ -59,6 +59,7 @@ export default function Reply(props: Props) { key={status.id} onRefresh={() => {}} openMedia={props.openMedia} + filters={[]} /> )} /> diff --git a/renderer/components/detail/Tag.tsx b/renderer/components/detail/Tag.tsx index 125d8c4d..a402cd67 100644 --- a/renderer/components/detail/Tag.tsx +++ b/renderer/components/detail/Tag.tsx @@ -38,6 +38,7 @@ export default function Tag(props: Props) { key={status.id} onRefresh={() => {}} openMedia={props.openMedia} + filters={[]} /> )} /> diff --git a/renderer/components/detail/Thread.tsx b/renderer/components/detail/Thread.tsx index 60627188..c92c572e 100644 --- a/renderer/components/detail/Thread.tsx +++ b/renderer/components/detail/Thread.tsx @@ -43,6 +43,7 @@ export default function Thread(props: Props) { key={status.id} onRefresh={() => {}} openMedia={props.openMedia} + filters={[]} /> )} /> diff --git a/renderer/components/detail/profile/Timeline.tsx b/renderer/components/detail/profile/Timeline.tsx index 46366ddb..58d9aa0e 100644 --- a/renderer/components/detail/profile/Timeline.tsx +++ b/renderer/components/detail/profile/Timeline.tsx @@ -50,6 +50,7 @@ export default function Timeline(props: Props) { key={index} onRefresh={status => setStatuses(current => updateStatus(current, status))} openMedia={props.openMedia} + filters={[]} /> ))} diff --git a/renderer/components/timelines/Notifications.tsx b/renderer/components/timelines/Notifications.tsx index bfebf71f..21c94b2d 100644 --- a/renderer/components/timelines/Notifications.tsx +++ b/renderer/components/timelines/Notifications.tsx @@ -27,6 +27,7 @@ export default function Notifications(props: Props) { const [firstItemIndex, setFirstItemIndex] = useState(TIMELINE_MAX_STATUSES) const [marker, setMarker] = useState(null) const [pleromaUnreads, setPleromaUnreads] = useState>([]) + const [filters, setFilters] = useState>([]) const scrollerRef = useRef(null) const streaming = useRef(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> => { + const res = await client.getFilters() + return res.data.filter(f => f.context.includes('notifications')) + } + const loadNotifications = async (client: MegalodonInterface, maxId?: string): Promise> => { 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} /> ) diff --git a/renderer/components/timelines/Timeline.tsx b/renderer/components/timelines/Timeline.tsx index 59514cf1..3f3d31f5 100644 --- a/renderer/components/timelines/Timeline.tsx +++ b/renderer/components/timelines/Timeline.tsx @@ -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(null) + const [filters, setFilters] = useState>([]) 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> => { + 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> => { 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} /> )} /> diff --git a/renderer/components/timelines/notification/Notification.tsx b/renderer/components/timelines/notification/Notification.tsx index cc9a0ffc..e6ac3a8b 100644 --- a/renderer/components/timelines/notification/Notification.tsx +++ b/renderer/components/timelines/notification/Notification.tsx @@ -10,6 +10,7 @@ type Props = { client: MegalodonInterface onRefresh: (status: Entity.Status) => void openMedia: (media: Entity.Attachment) => void + filters: Array } 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 { diff --git a/renderer/components/timelines/status/Status.tsx b/renderer/components/timelines/status/Status.tsx index 694c3049..b80b76ab 100644 --- a/renderer/components/timelines/status/Status.tsx +++ b/renderer/components/timelines/status/Status.tsx @@ -19,11 +19,13 @@ type Props = { client: MegalodonInterface onRefresh: (status: Entity.Status) => void openMedia: (media: Entity.Attachment) => void + filters: Array } 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 ( +
+ + setIgnoreFilter(true)}> + + +
+ ) + } + return (
{rebloggedHeader( diff --git a/renderer/provider/accounts.tsx b/renderer/provider/accounts.tsx index 6a33caf9..ced8d61f 100644 --- a/renderer/provider/accounts.tsx +++ b/renderer/provider/accounts.tsx @@ -64,15 +64,19 @@ export const AccountsProvider: React.FC = ({ 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')