From 53c8858fa67484af89ccd3c22928063e1bd65dcb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 14:41:50 -0500 Subject: [PATCH] Add useHashtagStream hook, clean up hashtags timeline (remove unused code for fetching multiple hashtags) --- app/soapbox/actions/streaming.ts | 4 - app/soapbox/api/hooks/index.ts | 1 + .../api/hooks/streaming/useHashtagStream.ts | 10 ++ .../features/hashtag-timeline/index.tsx | 94 +++---------------- 4 files changed, 23 insertions(+), 86 deletions(-) create mode 100644 app/soapbox/api/hooks/streaming/useHashtagStream.ts diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index c96176960..f050c7b15 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -190,13 +190,9 @@ const connectTimelineStream = ( }; }); -const connectHashtagStream = (id: string, tag: string, accept: (status: APIEntity) => boolean) => - connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept); - export { STREAMING_CHAT_UPDATE, STREAMING_FOLLOW_RELATIONSHIPS_UPDATE, connectTimelineStream, - connectHashtagStream, type TimelineStreamOpts, }; diff --git a/app/soapbox/api/hooks/index.ts b/app/soapbox/api/hooks/index.ts index 2809bd8f8..ee5733c9f 100644 --- a/app/soapbox/api/hooks/index.ts +++ b/app/soapbox/api/hooks/index.ts @@ -49,6 +49,7 @@ export { useUserStream } from './streaming/useUserStream'; export { useCommunityStream } from './streaming/useCommunityStream'; export { usePublicStream } from './streaming/usePublicStream'; export { useDirectStream } from './streaming/useDirectStream'; +export { useHashtagStream } from './streaming/useHashtagStream'; export { useListStream } from './streaming/useListStream'; export { useGroupStream } from './streaming/useGroupStream'; export { useRemoteStream } from './streaming/useRemoteStream'; diff --git a/app/soapbox/api/hooks/streaming/useHashtagStream.ts b/app/soapbox/api/hooks/streaming/useHashtagStream.ts new file mode 100644 index 000000000..4f9483bad --- /dev/null +++ b/app/soapbox/api/hooks/streaming/useHashtagStream.ts @@ -0,0 +1,10 @@ +import { useTimelineStream } from './useTimelineStream'; + +function useHashtagStream(tag: string) { + return useTimelineStream( + `hashtag:${tag}`, + `hashtag&tag=${tag}`, + ); +} + +export { useHashtagStream }; \ No newline at end of file diff --git a/app/soapbox/features/hashtag-timeline/index.tsx b/app/soapbox/features/hashtag-timeline/index.tsx index bf906ce01..69cc0cd60 100644 --- a/app/soapbox/features/hashtag-timeline/index.tsx +++ b/app/soapbox/features/hashtag-timeline/index.tsx @@ -1,96 +1,31 @@ -import React, { useEffect, useRef } from 'react'; -import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; +import React, { useEffect } from 'react'; +import { FormattedMessage } from 'react-intl'; -import { connectHashtagStream } from 'soapbox/actions/streaming'; import { fetchHashtag, followHashtag, unfollowHashtag } from 'soapbox/actions/tags'; import { expandHashtagTimeline, clearTimeline } from 'soapbox/actions/timelines'; +import { useHashtagStream } from 'soapbox/api/hooks'; import List, { ListItem } from 'soapbox/components/list'; import { Column, Toggle } from 'soapbox/components/ui'; import Timeline from 'soapbox/features/ui/components/timeline'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; -import type { Tag as TagEntity } from 'soapbox/types/entities'; - -type Mode = 'any' | 'all' | 'none'; - -type Tag = { value: string }; -type Tags = { [k in Mode]: Tag[] }; - -const messages = defineMessages({ - any: { id: 'hashtag.column_header.tag_mode.any', defaultMessage: 'or {additional}' }, - all: { id: 'hashtag.column_header.tag_mode.all', defaultMessage: 'and {additional}' }, - none: { id: 'hashtag.column_header.tag_mode.none', defaultMessage: 'without {additional}' }, - empty: { id: 'empty_column.hashtag', defaultMessage: 'There is nothing in this hashtag yet.' }, -}); - interface IHashtagTimeline { params?: { id?: string - tags?: Tags } } export const HashtagTimeline: React.FC = ({ params }) => { - const intl = useIntl(); const id = params?.id || ''; - const tags = params?.tags || { any: [], all: [], none: [] }; - + const features = useFeatures(); const dispatch = useAppDispatch(); - const disconnects = useRef<(() => void)[]>([]); const tag = useAppSelector((state) => state.tags.get(id)); const next = useAppSelector(state => state.timelines.get(`hashtag:${id}`)?.next); - // Mastodon supports displaying results from multiple hashtags. - // https://github.com/mastodon/mastodon/issues/6359 - const title = (): string => { - const title: string[] = [`#${id}`]; - - if (additionalFor('any')) { - title.push(' ', intl.formatMessage(messages.any, { additional: additionalFor('any') })); - } - - if (additionalFor('all')) { - title.push(' ', intl.formatMessage(messages.any, { additional: additionalFor('all') })); - } - - if (additionalFor('none')) { - title.push(' ', intl.formatMessage(messages.any, { additional: additionalFor('none') })); - } - - return title.join(''); - }; - - const additionalFor = (mode: Mode) => { - if (tags && (tags[mode] || []).length > 0) { - return tags[mode].map(tag => tag.value).join('/'); - } else { - return ''; - } - }; - - const subscribe = () => { - const any = tags.any.map(tag => tag.value); - const all = tags.all.map(tag => tag.value); - const none = tags.none.map(tag => tag.value); - - [id, ...any].map(tag => { - disconnects.current.push(dispatch(connectHashtagStream(id, tag, status => { - const tags = status.tags.map((tag: TagEntity) => tag.name); - - return all.filter(tag => tags.includes(tag)).length === all.length && - none.filter(tag => tags.includes(tag)).length === 0; - }))); - }); - }; - - const unsubscribe = () => { - disconnects.current.map(disconnect => disconnect()); - disconnects.current = []; - }; const handleLoadMore = (maxId: string) => { - dispatch(expandHashtagTimeline(id, { url: next, maxId, tags })); + dispatch(expandHashtagTimeline(id, { url: next, maxId })); }; const handleFollow = () => { @@ -101,25 +36,20 @@ export const HashtagTimeline: React.FC = ({ params }) => { } }; - useEffect(() => { - subscribe(); - dispatch(expandHashtagTimeline(id, { tags })); - dispatch(fetchHashtag(id)); + useHashtagStream(id); - return () => { - unsubscribe(); - }; + useEffect(() => { + dispatch(expandHashtagTimeline(id)); + dispatch(fetchHashtag(id)); }, []); useEffect(() => { - unsubscribe(); - subscribe(); dispatch(clearTimeline(`hashtag:${id}`)); - dispatch(expandHashtagTimeline(id, { tags })); + dispatch(expandHashtagTimeline(id)); }, [id]); return ( - + {features.followHashtags && ( = ({ params }) => { scrollKey='hashtag_timeline' timelineId={`hashtag:${id}`} onLoadMore={handleLoadMore} - emptyMessage={intl.formatMessage(messages.empty)} + emptyMessage={} divideType='space' />