From 77f0f4d3772b65db7851f90f7c7103c327cb1729 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 12:49:02 -0500 Subject: [PATCH 01/11] Add Streaming hooks --- app/soapbox/actions/streaming.ts | 8 ++-- app/soapbox/api/hooks/index.ts | 4 ++ .../api/hooks/streaming/useNostrStream.ts | 11 +++++ .../api/hooks/streaming/useTimelineStream.ts | 41 +++++++++++++++++ .../api/hooks/streaming/useUserStream.ts | 22 +++++++++ app/soapbox/features/ui/index.tsx | 46 ++----------------- 6 files changed, 88 insertions(+), 44 deletions(-) create mode 100644 app/soapbox/api/hooks/streaming/useNostrStream.ts create mode 100644 app/soapbox/api/hooks/streaming/useTimelineStream.ts create mode 100644 app/soapbox/api/hooks/streaming/useUserStream.ts diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index 65752c223..505c9445d 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -73,8 +73,9 @@ const updateChatQuery = (chat: IChat) => { queryClient.setQueryData(ChatKeys.chat(chat.id), newChat as any); }; -interface StreamOpts { +interface TimelineStreamOpts { statContext?: IStatContext + enabled?: boolean } const connectTimelineStream = ( @@ -82,7 +83,7 @@ const connectTimelineStream = ( path: string, pollingRefresh: ((dispatch: AppDispatch, done?: () => void) => void) | null = null, accept: ((status: APIEntity) => boolean) | null = null, - opts?: StreamOpts, + opts?: TimelineStreamOpts, ) => connectStream(path, pollingRefresh, (dispatch: AppDispatch, getState: () => RootState) => { const locale = getLocale(getState()); @@ -196,7 +197,7 @@ const refreshHomeTimelineAndNotification = (dispatch: AppDispatch, done?: () => dispatch(expandNotifications({}, () => dispatch(fetchAnnouncements(done)))))); -const connectUserStream = (opts?: StreamOpts) => +const connectUserStream = (opts?: TimelineStreamOpts) => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification, null, opts); const connectCommunityStream = ({ onlyMedia }: Record = {}) => @@ -236,4 +237,5 @@ export { connectListStream, connectGroupStream, connectNostrStream, + type TimelineStreamOpts, }; diff --git a/app/soapbox/api/hooks/index.ts b/app/soapbox/api/hooks/index.ts index e51a7d06c..47a9101a6 100644 --- a/app/soapbox/api/hooks/index.ts +++ b/app/soapbox/api/hooks/index.ts @@ -43,3 +43,7 @@ export { useSuggestedGroups } from './groups/useSuggestedGroups'; export { useUnmuteGroup } from './groups/useUnmuteGroup'; export { useUpdateGroup } from './groups/useUpdateGroup'; export { useUpdateGroupTag } from './groups/useUpdateGroupTag'; + +// Streaming +export { useUserStream } from './streaming/useUserStream'; +export { useNostrStream } from './streaming/useNostrStream'; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useNostrStream.ts b/app/soapbox/api/hooks/streaming/useNostrStream.ts new file mode 100644 index 000000000..3e6ef707f --- /dev/null +++ b/app/soapbox/api/hooks/streaming/useNostrStream.ts @@ -0,0 +1,11 @@ +import { useFeatures } from 'soapbox/hooks'; + +import { useTimelineStream } from './useTimelineStream'; + +function useNostrStream() { + const features = useFeatures(); + const enabled = features.nostrSign && Boolean(window.nostr); + return useTimelineStream('nostr', 'nostr', null, null, { enabled }); +} + +export { useNostrStream }; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useTimelineStream.ts b/app/soapbox/api/hooks/streaming/useTimelineStream.ts new file mode 100644 index 000000000..0e7a2a267 --- /dev/null +++ b/app/soapbox/api/hooks/streaming/useTimelineStream.ts @@ -0,0 +1,41 @@ +import { useEffect, useRef } from 'react'; + +import { connectTimelineStream } from 'soapbox/actions/streaming'; +import { useAppDispatch, useAppSelector, useInstance } from 'soapbox/hooks'; +import { getAccessToken } from 'soapbox/utils/auth'; + +function useTimelineStream(...args: Parameters) { + // TODO: get rid of streaming.ts and move the actual opts here. + const { enabled = true } = args[4] ?? {}; + + const dispatch = useAppDispatch(); + const instance = useInstance(); + const stream = useRef<(() => void) | null>(null); + + const accessToken = useAppSelector(getAccessToken); + const streamingUrl = instance.urls.get('streaming_api'); + + const connect = () => { + if (enabled && accessToken && streamingUrl && !stream.current) { + stream.current = dispatch(connectTimelineStream(...args)); + } + }; + + const disconnect = () => { + if (stream.current) { + stream.current(); + stream.current = null; + } + }; + + useEffect(() => { + connect(); + return disconnect; + }, [accessToken, streamingUrl, enabled]); + + return { + disconnect, + }; +} + +export { useTimelineStream }; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useUserStream.ts b/app/soapbox/api/hooks/streaming/useUserStream.ts new file mode 100644 index 000000000..5e0bb9aed --- /dev/null +++ b/app/soapbox/api/hooks/streaming/useUserStream.ts @@ -0,0 +1,22 @@ +import { fetchAnnouncements } from 'soapbox/actions/announcements'; +import { expandNotifications } from 'soapbox/actions/notifications'; +import { expandHomeTimeline } from 'soapbox/actions/timelines'; +import { useStatContext } from 'soapbox/contexts/stat-context'; + +import { useTimelineStream } from './useTimelineStream'; + +import type { AppDispatch } from 'soapbox/store'; + +function useUserStream() { + const statContext = useStatContext(); + return useTimelineStream('home', 'user', refresh, null, { statContext }); +} + +/** Refresh home timeline and notifications. */ +function refresh(dispatch: AppDispatch, done?: () => void) { + return dispatch(expandHomeTimeline({}, () => + dispatch(expandNotifications({}, () => + dispatch(fetchAnnouncements(done)))))); +} + +export { useUserStream }; \ No newline at end of file diff --git a/app/soapbox/features/ui/index.tsx b/app/soapbox/features/ui/index.tsx index 9da22891b..16e1861f3 100644 --- a/app/soapbox/features/ui/index.tsx +++ b/app/soapbox/features/ui/index.tsx @@ -14,16 +14,15 @@ import { openModal } from 'soapbox/actions/modals'; import { expandNotifications } from 'soapbox/actions/notifications'; import { register as registerPushNotifications } from 'soapbox/actions/push-notifications'; import { fetchScheduledStatuses } from 'soapbox/actions/scheduled-statuses'; -import { connectNostrStream, connectUserStream } from 'soapbox/actions/streaming'; import { fetchSuggestionsForTimeline } from 'soapbox/actions/suggestions'; import { expandHomeTimeline } from 'soapbox/actions/timelines'; +import { useNostrStream, useUserStream } from 'soapbox/api/hooks'; import GroupLookupHoc from 'soapbox/components/hoc/group-lookup-hoc'; import withHoc from 'soapbox/components/hoc/with-hoc'; import SidebarNavigation from 'soapbox/components/sidebar-navigation'; import ThumbNavigation from 'soapbox/components/thumb-navigation'; import { Layout } from 'soapbox/components/ui'; -import { useStatContext } from 'soapbox/contexts/stat-context'; -import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures, useInstance, useDraggedFiles } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures, useDraggedFiles } from 'soapbox/hooks'; import AdminPage from 'soapbox/pages/admin-page'; import ChatsPage from 'soapbox/pages/chats-page'; import DefaultPage from 'soapbox/pages/default-page'; @@ -39,7 +38,7 @@ import RemoteInstancePage from 'soapbox/pages/remote-instance-page'; import SearchPage from 'soapbox/pages/search-page'; import StatusPage from 'soapbox/pages/status-page'; import { usePendingPolicy } from 'soapbox/queries/policies'; -import { getAccessToken, getVapidKey } from 'soapbox/utils/auth'; +import { getVapidKey } from 'soapbox/utils/auth'; import { isStandalone } from 'soapbox/utils/state'; import BackgroundShapes from './components/background-shapes'; @@ -363,21 +362,13 @@ const UI: React.FC = ({ children }) => { const history = useHistory(); const dispatch = useAppDispatch(); const { data: pendingPolicy } = usePendingPolicy(); - const instance = useInstance(); - const statContext = useStatContext(); - - const userStream = useRef(null); - const nostrStream = useRef(null); const node = useRef(null); - const me = useAppSelector(state => state.me); const { account } = useOwnAccount(); const features = useFeatures(); const vapidKey = useAppSelector(state => getVapidKey(state)); const dropdownMenuIsOpen = useAppSelector(state => state.dropdown_menu.isOpen); - const accessToken = useAppSelector(state => getAccessToken(state)); - const streamingUrl = instance.urls.get('streaming_api'); const standalone = useAppSelector(isStandalone); const { isDragging } = useDraggedFiles(node); @@ -390,28 +381,6 @@ const UI: React.FC = ({ children }) => { } }; - const connectStreaming = () => { - if (accessToken && streamingUrl) { - if (!userStream.current) { - userStream.current = dispatch(connectUserStream({ statContext })); - } - if (!nostrStream.current && features.nostrSign && window.nostr) { - nostrStream.current = dispatch(connectNostrStream()); - } - } - }; - - const disconnectStreaming = () => { - if (userStream.current) { - userStream.current(); - userStream.current = null; - } - if (nostrStream.current) { - nostrStream.current(); - nostrStream.current = null; - } - }; - const handleDragEnter = (e: DragEvent) => e.preventDefault(); const handleDragLeave = (e: DragEvent) => e.preventDefault(); const handleDragOver = (e: DragEvent) => e.preventDefault(); @@ -458,10 +427,6 @@ const UI: React.FC = ({ children }) => { if (window.Notification?.permission === 'default') { window.setTimeout(() => Notification.requestPermission(), 120 * 1000); } - - return () => { - disconnectStreaming(); - }; }, []); useEffect(() => { @@ -477,9 +442,8 @@ const UI: React.FC = ({ children }) => { }; }, []); - useEffect(() => { - connectStreaming(); - }, [accessToken, streamingUrl]); + useUserStream(); + useNostrStream(); // The user has logged in useEffect(() => { From 757f6600f88989dfbe76f53168564442daa2e40d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 12:53:06 -0500 Subject: [PATCH 02/11] Streaming: remove unused code --- app/soapbox/actions/streaming.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index 505c9445d..1e5e65060 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -10,18 +10,16 @@ import { connectStream } from '../stream'; import { deleteAnnouncement, - fetchAnnouncements, updateAnnouncements, updateReaction as updateAnnouncementsReaction, } from './announcements'; import { updateConversations } from './conversations'; import { fetchFilters } from './filters'; import { MARKER_FETCH_SUCCESS } from './markers'; -import { updateNotificationsQueue, expandNotifications } from './notifications'; +import { updateNotificationsQueue } from './notifications'; import { updateStatus } from './statuses'; import { // deleteFromTimelines, - expandHomeTimeline, connectTimeline, disconnectTimeline, processTimelineUpdate, @@ -192,14 +190,6 @@ const connectTimelineStream = ( }; }); -const refreshHomeTimelineAndNotification = (dispatch: AppDispatch, done?: () => void) => - dispatch(expandHomeTimeline({}, () => - dispatch(expandNotifications({}, () => - dispatch(fetchAnnouncements(done)))))); - -const connectUserStream = (opts?: TimelineStreamOpts) => - connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification, null, opts); - const connectCommunityStream = ({ onlyMedia }: Record = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`); @@ -221,14 +211,10 @@ const connectListStream = (id: string) => const connectGroupStream = (id: string) => connectTimelineStream(`group:${id}`, `group&group=${id}`); -const connectNostrStream = () => - connectTimelineStream('nostr', 'nostr'); - export { STREAMING_CHAT_UPDATE, STREAMING_FOLLOW_RELATIONSHIPS_UPDATE, connectTimelineStream, - connectUserStream, connectCommunityStream, connectPublicStream, connectRemoteStream, @@ -236,6 +222,5 @@ export { connectDirectStream, connectListStream, connectGroupStream, - connectNostrStream, type TimelineStreamOpts, }; From 3cef200a44c9cadeff6a4573f0b49edb25932789 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 13:05:41 -0500 Subject: [PATCH 03/11] Add useCommunityStream hook, refresh socket when timelineId or path changes --- app/soapbox/actions/streaming.ts | 4 ---- app/soapbox/api/hooks/index.ts | 1 + .../api/hooks/streaming/useCommunityStream.ts | 14 ++++++++++++++ .../api/hooks/streaming/useTimelineStream.ts | 3 ++- app/soapbox/features/community-timeline/index.tsx | 15 ++++++--------- 5 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 app/soapbox/api/hooks/streaming/useCommunityStream.ts diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index 1e5e65060..90b73569e 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -190,9 +190,6 @@ const connectTimelineStream = ( }; }); -const connectCommunityStream = ({ onlyMedia }: Record = {}) => - connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`); - const connectPublicStream = ({ onlyMedia }: Record = {}) => connectTimelineStream(`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`); @@ -215,7 +212,6 @@ export { STREAMING_CHAT_UPDATE, STREAMING_FOLLOW_RELATIONSHIPS_UPDATE, connectTimelineStream, - connectCommunityStream, connectPublicStream, connectRemoteStream, connectHashtagStream, diff --git a/app/soapbox/api/hooks/index.ts b/app/soapbox/api/hooks/index.ts index 47a9101a6..6cd1802ba 100644 --- a/app/soapbox/api/hooks/index.ts +++ b/app/soapbox/api/hooks/index.ts @@ -46,4 +46,5 @@ export { useUpdateGroupTag } from './groups/useUpdateGroupTag'; // Streaming export { useUserStream } from './streaming/useUserStream'; +export { useCommunityStream } from './streaming/useCommunityStream'; export { useNostrStream } from './streaming/useNostrStream'; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useCommunityStream.ts b/app/soapbox/api/hooks/streaming/useCommunityStream.ts new file mode 100644 index 000000000..f0ccca5d6 --- /dev/null +++ b/app/soapbox/api/hooks/streaming/useCommunityStream.ts @@ -0,0 +1,14 @@ +import { useTimelineStream } from './useTimelineStream'; + +interface UseCommunityStreamOpts { + onlyMedia?: boolean +} + +function useCommunityStream({ onlyMedia }: UseCommunityStreamOpts = {}) { + return useTimelineStream( + `community${onlyMedia ? ':media' : ''}`, + `public:local${onlyMedia ? ':media' : ''}`, + ); +} + +export { useCommunityStream }; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useTimelineStream.ts b/app/soapbox/api/hooks/streaming/useTimelineStream.ts index 0e7a2a267..a0dfb2ded 100644 --- a/app/soapbox/api/hooks/streaming/useTimelineStream.ts +++ b/app/soapbox/api/hooks/streaming/useTimelineStream.ts @@ -6,6 +6,7 @@ import { getAccessToken } from 'soapbox/utils/auth'; function useTimelineStream(...args: Parameters) { // TODO: get rid of streaming.ts and move the actual opts here. + const [timelineId, path] = args; const { enabled = true } = args[4] ?? {}; const dispatch = useAppDispatch(); @@ -31,7 +32,7 @@ function useTimelineStream(...args: Parameters) { useEffect(() => { connect(); return disconnect; - }, [accessToken, streamingUrl, enabled]); + }, [accessToken, streamingUrl, timelineId, path, enabled]); return { disconnect, diff --git a/app/soapbox/features/community-timeline/index.tsx b/app/soapbox/features/community-timeline/index.tsx index 387297a80..37f4e8dd2 100644 --- a/app/soapbox/features/community-timeline/index.tsx +++ b/app/soapbox/features/community-timeline/index.tsx @@ -1,8 +1,8 @@ import React, { useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { connectCommunityStream } from 'soapbox/actions/streaming'; import { expandCommunityTimeline } from 'soapbox/actions/timelines'; +import { useCommunityStream } from 'soapbox/api/hooks'; import PullToRefresh from 'soapbox/components/pull-to-refresh'; import { Column } from 'soapbox/components/ui'; import { useAppSelector, useAppDispatch, useSettings } from 'soapbox/hooks'; @@ -18,7 +18,7 @@ const CommunityTimeline = () => { const dispatch = useAppDispatch(); const settings = useSettings(); - const onlyMedia = settings.getIn(['community', 'other', 'onlyMedia']); + const onlyMedia = !!settings.getIn(['community', 'other', 'onlyMedia'], false); const next = useAppSelector(state => state.timelines.get('community')?.next); const timelineId = 'community'; @@ -28,16 +28,13 @@ const CommunityTimeline = () => { }; const handleRefresh = () => { - return dispatch(expandCommunityTimeline({ onlyMedia } as any)); + return dispatch(expandCommunityTimeline({ onlyMedia })); }; - useEffect(() => { - dispatch(expandCommunityTimeline({ onlyMedia } as any)); - const disconnect = dispatch(connectCommunityStream({ onlyMedia } as any)); + useCommunityStream({ onlyMedia }); - return () => { - disconnect(); - }; + useEffect(() => { + dispatch(expandCommunityTimeline({ onlyMedia })); }, [onlyMedia]); return ( From d99e266008875237998bae736b2398ad0f77ce1c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 13:09:45 -0500 Subject: [PATCH 04/11] Add usePublicStream hook --- app/soapbox/actions/streaming.ts | 4 ---- app/soapbox/api/hooks/index.ts | 1 + .../api/hooks/streaming/usePublicStream.ts | 14 ++++++++++++++ app/soapbox/features/public-timeline/index.tsx | 15 ++++++--------- 4 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 app/soapbox/api/hooks/streaming/usePublicStream.ts diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index 90b73569e..b828c5818 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -190,9 +190,6 @@ const connectTimelineStream = ( }; }); -const connectPublicStream = ({ onlyMedia }: Record = {}) => - connectTimelineStream(`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`); - const connectRemoteStream = (instance: string, { onlyMedia }: Record = {}) => connectTimelineStream(`remote${onlyMedia ? ':media' : ''}:${instance}`, `public:remote${onlyMedia ? ':media' : ''}&instance=${instance}`); @@ -212,7 +209,6 @@ export { STREAMING_CHAT_UPDATE, STREAMING_FOLLOW_RELATIONSHIPS_UPDATE, connectTimelineStream, - connectPublicStream, connectRemoteStream, connectHashtagStream, connectDirectStream, diff --git a/app/soapbox/api/hooks/index.ts b/app/soapbox/api/hooks/index.ts index 6cd1802ba..ea473df1b 100644 --- a/app/soapbox/api/hooks/index.ts +++ b/app/soapbox/api/hooks/index.ts @@ -47,4 +47,5 @@ export { useUpdateGroupTag } from './groups/useUpdateGroupTag'; // Streaming export { useUserStream } from './streaming/useUserStream'; export { useCommunityStream } from './streaming/useCommunityStream'; +export { usePublicStream } from './streaming/usePublicStream'; export { useNostrStream } from './streaming/useNostrStream'; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/usePublicStream.ts b/app/soapbox/api/hooks/streaming/usePublicStream.ts new file mode 100644 index 000000000..eb189c996 --- /dev/null +++ b/app/soapbox/api/hooks/streaming/usePublicStream.ts @@ -0,0 +1,14 @@ +import { useTimelineStream } from './useTimelineStream'; + +interface UsePublicStreamOpts { + onlyMedia?: boolean +} + +function usePublicStream({ onlyMedia }: UsePublicStreamOpts = {}) { + return useTimelineStream( + `public${onlyMedia ? ':media' : ''}`, + `public${onlyMedia ? ':media' : ''}`, + ); +} + +export { usePublicStream }; \ No newline at end of file diff --git a/app/soapbox/features/public-timeline/index.tsx b/app/soapbox/features/public-timeline/index.tsx index cad8cd7f6..b08c2ed6f 100644 --- a/app/soapbox/features/public-timeline/index.tsx +++ b/app/soapbox/features/public-timeline/index.tsx @@ -3,8 +3,8 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { Link } from 'react-router-dom'; import { changeSetting } from 'soapbox/actions/settings'; -import { connectPublicStream } from 'soapbox/actions/streaming'; import { expandPublicTimeline } from 'soapbox/actions/timelines'; +import { usePublicStream } from 'soapbox/api/hooks'; import PullToRefresh from 'soapbox/components/pull-to-refresh'; import { Accordion, Column } from 'soapbox/components/ui'; import { useAppSelector, useAppDispatch, useInstance, useSettings } from 'soapbox/hooks'; @@ -23,7 +23,7 @@ const CommunityTimeline = () => { const instance = useInstance(); const settings = useSettings(); - const onlyMedia = settings.getIn(['public', 'other', 'onlyMedia']); + const onlyMedia = !!settings.getIn(['public', 'other', 'onlyMedia'], false); const next = useAppSelector(state => state.timelines.get('public')?.next); const timelineId = 'public'; @@ -44,16 +44,13 @@ const CommunityTimeline = () => { }; const handleRefresh = () => { - return dispatch(expandPublicTimeline({ onlyMedia } as any)); + return dispatch(expandPublicTimeline({ onlyMedia })); }; - useEffect(() => { - dispatch(expandPublicTimeline({ onlyMedia } as any)); - const disconnect = dispatch(connectPublicStream({ onlyMedia })); + usePublicStream({ onlyMedia }); - return () => { - disconnect(); - }; + useEffect(() => { + dispatch(expandPublicTimeline({ onlyMedia })); }, [onlyMedia]); return ( From 9b1352f0adbaa7ad47bafe8d7d4347ba1b8c7112 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 13:16:01 -0500 Subject: [PATCH 05/11] Add useRemoteStream hook --- app/soapbox/actions/streaming.ts | 4 ---- app/soapbox/api/hooks/index.ts | 1 + .../api/hooks/streaming/useRemoteStream.ts | 15 ++++++++++++ .../features/remote-timeline/index.tsx | 23 ++++--------------- 4 files changed, 21 insertions(+), 22 deletions(-) create mode 100644 app/soapbox/api/hooks/streaming/useRemoteStream.ts diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index b828c5818..8173752cf 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -190,9 +190,6 @@ const connectTimelineStream = ( }; }); -const connectRemoteStream = (instance: string, { onlyMedia }: Record = {}) => - connectTimelineStream(`remote${onlyMedia ? ':media' : ''}:${instance}`, `public:remote${onlyMedia ? ':media' : ''}&instance=${instance}`); - const connectHashtagStream = (id: string, tag: string, accept: (status: APIEntity) => boolean) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept); @@ -209,7 +206,6 @@ export { STREAMING_CHAT_UPDATE, STREAMING_FOLLOW_RELATIONSHIPS_UPDATE, connectTimelineStream, - connectRemoteStream, connectHashtagStream, connectDirectStream, connectListStream, diff --git a/app/soapbox/api/hooks/index.ts b/app/soapbox/api/hooks/index.ts index ea473df1b..9569318a7 100644 --- a/app/soapbox/api/hooks/index.ts +++ b/app/soapbox/api/hooks/index.ts @@ -48,4 +48,5 @@ export { useUpdateGroupTag } from './groups/useUpdateGroupTag'; export { useUserStream } from './streaming/useUserStream'; export { useCommunityStream } from './streaming/useCommunityStream'; export { usePublicStream } from './streaming/usePublicStream'; +export { useRemoteStream } from './streaming/useRemoteStream'; export { useNostrStream } from './streaming/useNostrStream'; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useRemoteStream.ts b/app/soapbox/api/hooks/streaming/useRemoteStream.ts new file mode 100644 index 000000000..f67f99083 --- /dev/null +++ b/app/soapbox/api/hooks/streaming/useRemoteStream.ts @@ -0,0 +1,15 @@ +import { useTimelineStream } from './useTimelineStream'; + +interface UseRemoteStreamOpts { + instance: string + onlyMedia?: boolean +} + +function useRemoteStream({ instance, onlyMedia }: UseRemoteStreamOpts) { + return useTimelineStream( + `remote${onlyMedia ? ':media' : ''}:${instance}`, + `public:remote${onlyMedia ? ':media' : ''}&instance=${instance}`, + ); +} + +export { useRemoteStream }; \ No newline at end of file diff --git a/app/soapbox/features/remote-timeline/index.tsx b/app/soapbox/features/remote-timeline/index.tsx index b0afd38a8..87fed22ec 100644 --- a/app/soapbox/features/remote-timeline/index.tsx +++ b/app/soapbox/features/remote-timeline/index.tsx @@ -1,9 +1,9 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; import { useHistory } from 'react-router-dom'; -import { connectRemoteStream } from 'soapbox/actions/streaming'; import { expandRemoteTimeline } from 'soapbox/actions/timelines'; +import { useRemoteStream } from 'soapbox/api/hooks'; import IconButton from 'soapbox/components/icon-button'; import { Column, HStack, Text } from 'soapbox/components/ui'; import { useAppSelector, useAppDispatch, useSettings } from 'soapbox/hooks'; @@ -26,20 +26,12 @@ const RemoteTimeline: React.FC = ({ params }) => { const instance = params?.instance as string; const settings = useSettings(); - const stream = useRef(null); - const timelineId = 'remote'; const onlyMedia = !!settings.getIn(['remote', 'other', 'onlyMedia']); const next = useAppSelector(state => state.timelines.get('remote')?.next); const pinned: boolean = (settings.getIn(['remote_timeline', 'pinnedHosts']) as any).includes(instance); - const disconnect = () => { - if (stream.current) { - stream.current(); - } - }; - const handleCloseClick: React.MouseEventHandler = () => { history.push('/timeline/fediverse'); }; @@ -48,15 +40,10 @@ const RemoteTimeline: React.FC = ({ params }) => { dispatch(expandRemoteTimeline(instance, { url: next, maxId, onlyMedia })); }; - useEffect(() => { - disconnect(); - dispatch(expandRemoteTimeline(instance, { onlyMedia, maxId: undefined })); - stream.current = dispatch(connectRemoteStream(instance, { onlyMedia })); + useRemoteStream({ instance, onlyMedia }); - return () => { - disconnect(); - stream.current = null; - }; + useEffect(() => { + dispatch(expandRemoteTimeline(instance, { onlyMedia, maxId: undefined })); }, [onlyMedia]); return ( From 4090d6ab51c11682d43bff92ca576e093e62e4a2 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 14:00:00 -0500 Subject: [PATCH 06/11] Add useDirectStream hook --- app/soapbox/actions/streaming.ts | 4 ---- app/soapbox/api/hooks/index.ts | 1 + app/soapbox/api/hooks/streaming/useDirectStream.ts | 7 +++++++ app/soapbox/features/conversations/index.tsx | 7 +++---- app/soapbox/features/direct-timeline/index.tsx | 9 +++------ 5 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 app/soapbox/api/hooks/streaming/useDirectStream.ts diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index 8173752cf..1d71d8d98 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -193,9 +193,6 @@ const connectTimelineStream = ( const connectHashtagStream = (id: string, tag: string, accept: (status: APIEntity) => boolean) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept); -const connectDirectStream = () => - connectTimelineStream('direct', 'direct'); - const connectListStream = (id: string) => connectTimelineStream(`list:${id}`, `list&list=${id}`); @@ -207,7 +204,6 @@ export { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE, connectTimelineStream, connectHashtagStream, - connectDirectStream, connectListStream, connectGroupStream, type TimelineStreamOpts, diff --git a/app/soapbox/api/hooks/index.ts b/app/soapbox/api/hooks/index.ts index 9569318a7..214607f6d 100644 --- a/app/soapbox/api/hooks/index.ts +++ b/app/soapbox/api/hooks/index.ts @@ -48,5 +48,6 @@ export { useUpdateGroupTag } from './groups/useUpdateGroupTag'; export { useUserStream } from './streaming/useUserStream'; export { useCommunityStream } from './streaming/useCommunityStream'; export { usePublicStream } from './streaming/usePublicStream'; +export { useDirectStream } from './streaming/useDirectStream'; export { useRemoteStream } from './streaming/useRemoteStream'; export { useNostrStream } from './streaming/useNostrStream'; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useDirectStream.ts b/app/soapbox/api/hooks/streaming/useDirectStream.ts new file mode 100644 index 000000000..e97fc8860 --- /dev/null +++ b/app/soapbox/api/hooks/streaming/useDirectStream.ts @@ -0,0 +1,7 @@ +import { useTimelineStream } from './useTimelineStream'; + +function useDirectStream() { + return useTimelineStream('direct', 'direct'); +} + +export { useDirectStream }; \ No newline at end of file diff --git a/app/soapbox/features/conversations/index.tsx b/app/soapbox/features/conversations/index.tsx index 12b418eb0..1f6774ab9 100644 --- a/app/soapbox/features/conversations/index.tsx +++ b/app/soapbox/features/conversations/index.tsx @@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { directComposeById } from 'soapbox/actions/compose'; import { mountConversations, unmountConversations, expandConversations } from 'soapbox/actions/conversations'; -import { connectDirectStream } from 'soapbox/actions/streaming'; +import { useDirectStream } from 'soapbox/api/hooks'; import AccountSearch from 'soapbox/components/account-search'; import { Column } from 'soapbox/components/ui'; import { useAppDispatch } from 'soapbox/hooks'; @@ -19,15 +19,14 @@ const ConversationsTimeline = () => { const intl = useIntl(); const dispatch = useAppDispatch(); + useDirectStream(); + useEffect(() => { dispatch(mountConversations()); dispatch(expandConversations()); - const disconnect = dispatch(connectDirectStream()); - return () => { dispatch(unmountConversations()); - disconnect(); }; }, []); diff --git a/app/soapbox/features/direct-timeline/index.tsx b/app/soapbox/features/direct-timeline/index.tsx index eee31a829..ba8ba1cb0 100644 --- a/app/soapbox/features/direct-timeline/index.tsx +++ b/app/soapbox/features/direct-timeline/index.tsx @@ -2,8 +2,8 @@ import React, { useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { directComposeById } from 'soapbox/actions/compose'; -import { connectDirectStream } from 'soapbox/actions/streaming'; import { expandDirectTimeline } from 'soapbox/actions/timelines'; +import { useDirectStream } from 'soapbox/api/hooks'; import AccountSearch from 'soapbox/components/account-search'; import { Column } from 'soapbox/components/ui'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; @@ -20,13 +20,10 @@ const DirectTimeline = () => { const dispatch = useAppDispatch(); const next = useAppSelector(state => state.timelines.get('direct')?.next); + useDirectStream(); + useEffect(() => { dispatch(expandDirectTimeline()); - const disconnect = dispatch(connectDirectStream()); - - return (() => { - disconnect(); - }); }, []); const handleSuggestion = (accountId: string) => { From 811a9af670bc3ede47cb4ad001f6aa2a7e64e586 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 14:03:00 -0500 Subject: [PATCH 07/11] Add useListStream hook --- app/soapbox/actions/streaming.ts | 4 ---- app/soapbox/api/hooks/index.ts | 1 + app/soapbox/api/hooks/streaming/useListStream.ts | 10 ++++++++++ app/soapbox/features/list-timeline/index.tsx | 10 +++------- 4 files changed, 14 insertions(+), 11 deletions(-) create mode 100644 app/soapbox/api/hooks/streaming/useListStream.ts diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index 1d71d8d98..1367187eb 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -193,9 +193,6 @@ const connectTimelineStream = ( const connectHashtagStream = (id: string, tag: string, accept: (status: APIEntity) => boolean) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept); -const connectListStream = (id: string) => - connectTimelineStream(`list:${id}`, `list&list=${id}`); - const connectGroupStream = (id: string) => connectTimelineStream(`group:${id}`, `group&group=${id}`); @@ -204,7 +201,6 @@ export { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE, connectTimelineStream, connectHashtagStream, - connectListStream, connectGroupStream, type TimelineStreamOpts, }; diff --git a/app/soapbox/api/hooks/index.ts b/app/soapbox/api/hooks/index.ts index 214607f6d..1983c59f2 100644 --- a/app/soapbox/api/hooks/index.ts +++ b/app/soapbox/api/hooks/index.ts @@ -49,5 +49,6 @@ export { useUserStream } from './streaming/useUserStream'; export { useCommunityStream } from './streaming/useCommunityStream'; export { usePublicStream } from './streaming/usePublicStream'; export { useDirectStream } from './streaming/useDirectStream'; +export { useListStream } from './streaming/useListStream'; export { useRemoteStream } from './streaming/useRemoteStream'; export { useNostrStream } from './streaming/useNostrStream'; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useListStream.ts b/app/soapbox/api/hooks/streaming/useListStream.ts new file mode 100644 index 000000000..f38b0209c --- /dev/null +++ b/app/soapbox/api/hooks/streaming/useListStream.ts @@ -0,0 +1,10 @@ +import { useTimelineStream } from './useTimelineStream'; + +function useListStream(listId: string) { + return useTimelineStream( + `list:${listId}`, + `list&list=${listId}`, + ); +} + +export { useListStream }; \ No newline at end of file diff --git a/app/soapbox/features/list-timeline/index.tsx b/app/soapbox/features/list-timeline/index.tsx index f16acf18b..e9b56aada 100644 --- a/app/soapbox/features/list-timeline/index.tsx +++ b/app/soapbox/features/list-timeline/index.tsx @@ -4,8 +4,8 @@ import { useParams } from 'react-router-dom'; import { fetchList } from 'soapbox/actions/lists'; import { openModal } from 'soapbox/actions/modals'; -import { connectListStream } from 'soapbox/actions/streaming'; import { expandListTimeline } from 'soapbox/actions/timelines'; +import { useListStream } from 'soapbox/api/hooks'; import MissingIndicator from 'soapbox/components/missing-indicator'; import { Column, Button, Spinner } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; @@ -19,15 +19,11 @@ const ListTimeline: React.FC = () => { const list = useAppSelector((state) => state.lists.get(id)); const next = useAppSelector(state => state.timelines.get(`list:${id}`)?.next); + useListStream(id); + useEffect(() => { dispatch(fetchList(id)); dispatch(expandListTimeline(id)); - - const disconnect = dispatch(connectListStream(id)); - - return () => { - disconnect(); - }; }, [id]); const handleLoadMore = (maxId: string) => { From 4a4a2d1a8713940a3d250779f1ba945d43dcbf2a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 14:06:15 -0500 Subject: [PATCH 08/11] Add useGroupStream hook --- app/soapbox/actions/streaming.ts | 4 ---- app/soapbox/api/hooks/index.ts | 1 + app/soapbox/api/hooks/streaming/useGroupStream.ts | 10 ++++++++++ app/soapbox/features/group/group-timeline.tsx | 11 +++-------- 4 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 app/soapbox/api/hooks/streaming/useGroupStream.ts diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index 1367187eb..c96176960 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -193,14 +193,10 @@ const connectTimelineStream = ( const connectHashtagStream = (id: string, tag: string, accept: (status: APIEntity) => boolean) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept); -const connectGroupStream = (id: string) => - connectTimelineStream(`group:${id}`, `group&group=${id}`); - export { STREAMING_CHAT_UPDATE, STREAMING_FOLLOW_RELATIONSHIPS_UPDATE, connectTimelineStream, connectHashtagStream, - connectGroupStream, type TimelineStreamOpts, }; diff --git a/app/soapbox/api/hooks/index.ts b/app/soapbox/api/hooks/index.ts index 1983c59f2..2809bd8f8 100644 --- a/app/soapbox/api/hooks/index.ts +++ b/app/soapbox/api/hooks/index.ts @@ -50,5 +50,6 @@ export { useCommunityStream } from './streaming/useCommunityStream'; export { usePublicStream } from './streaming/usePublicStream'; export { useDirectStream } from './streaming/useDirectStream'; export { useListStream } from './streaming/useListStream'; +export { useGroupStream } from './streaming/useGroupStream'; export { useRemoteStream } from './streaming/useRemoteStream'; export { useNostrStream } from './streaming/useNostrStream'; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useGroupStream.ts b/app/soapbox/api/hooks/streaming/useGroupStream.ts new file mode 100644 index 000000000..f9db3f69e --- /dev/null +++ b/app/soapbox/api/hooks/streaming/useGroupStream.ts @@ -0,0 +1,10 @@ +import { useTimelineStream } from './useTimelineStream'; + +function useGroupStream(groupId: string) { + return useTimelineStream( + `group:${groupId}`, + `group&group=${groupId}`, + ); +} + +export { useGroupStream }; \ No newline at end of file diff --git a/app/soapbox/features/group/group-timeline.tsx b/app/soapbox/features/group/group-timeline.tsx index 75b7a95d3..b10c41f9e 100644 --- a/app/soapbox/features/group/group-timeline.tsx +++ b/app/soapbox/features/group/group-timeline.tsx @@ -4,9 +4,8 @@ import { FormattedMessage, useIntl } from 'react-intl'; import { Link } from 'react-router-dom'; import { groupCompose, setGroupTimelineVisible, uploadCompose } from 'soapbox/actions/compose'; -import { connectGroupStream } from 'soapbox/actions/streaming'; import { expandGroupFeaturedTimeline, expandGroupTimeline } from 'soapbox/actions/timelines'; -import { useGroup } from 'soapbox/api/hooks'; +import { useGroup, useGroupStream } from 'soapbox/api/hooks'; import { Avatar, HStack, Icon, Stack, Text, Toggle } from 'soapbox/components/ui'; import ComposeForm from 'soapbox/features/compose/components/compose-form'; import { useAppDispatch, useAppSelector, useDraggedFiles, useOwnAccount } from 'soapbox/hooks'; @@ -49,16 +48,12 @@ const GroupTimeline: React.FC = (props) => { dispatch(setGroupTimelineVisible(composeId, !groupTimelineVisible)); }; + useGroupStream(groupId); + useEffect(() => { dispatch(expandGroupTimeline(groupId)); dispatch(expandGroupFeaturedTimeline(groupId)); dispatch(groupCompose(composeId, groupId)); - - const disconnect = dispatch(connectGroupStream(groupId)); - - return () => { - disconnect(); - }; }, [groupId]); if (!group) { From 53c8858fa67484af89ccd3c22928063e1bd65dcb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 14:41:50 -0500 Subject: [PATCH 09/11] 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' /> From 85c8f674b4926d03ac8edfbf3622ea76c9289850 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 14:55:21 -0500 Subject: [PATCH 10/11] Streaming: allow connecting if not logged in, gate certain topics --- .../api/hooks/streaming/useDirectStream.ts | 12 +++++++++++- app/soapbox/api/hooks/streaming/useListStream.ts | 7 +++++++ app/soapbox/api/hooks/streaming/useNostrStream.ts | 15 ++++++++++++--- .../api/hooks/streaming/useTimelineStream.ts | 2 +- app/soapbox/api/hooks/streaming/useUserStream.ts | 11 ++++++++++- 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/app/soapbox/api/hooks/streaming/useDirectStream.ts b/app/soapbox/api/hooks/streaming/useDirectStream.ts index e97fc8860..9d3b47853 100644 --- a/app/soapbox/api/hooks/streaming/useDirectStream.ts +++ b/app/soapbox/api/hooks/streaming/useDirectStream.ts @@ -1,7 +1,17 @@ +import { useLoggedIn } from 'soapbox/hooks'; + import { useTimelineStream } from './useTimelineStream'; function useDirectStream() { - return useTimelineStream('direct', 'direct'); + const { isLoggedIn } = useLoggedIn(); + + return useTimelineStream( + 'direct', + 'direct', + null, + null, + { enabled: isLoggedIn }, + ); } export { useDirectStream }; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useListStream.ts b/app/soapbox/api/hooks/streaming/useListStream.ts index f38b0209c..661bdce4f 100644 --- a/app/soapbox/api/hooks/streaming/useListStream.ts +++ b/app/soapbox/api/hooks/streaming/useListStream.ts @@ -1,9 +1,16 @@ +import { useLoggedIn } from 'soapbox/hooks'; + import { useTimelineStream } from './useTimelineStream'; function useListStream(listId: string) { + const { isLoggedIn } = useLoggedIn(); + return useTimelineStream( `list:${listId}`, `list&list=${listId}`, + null, + null, + { enabled: isLoggedIn }, ); } diff --git a/app/soapbox/api/hooks/streaming/useNostrStream.ts b/app/soapbox/api/hooks/streaming/useNostrStream.ts index 3e6ef707f..6748f95ea 100644 --- a/app/soapbox/api/hooks/streaming/useNostrStream.ts +++ b/app/soapbox/api/hooks/streaming/useNostrStream.ts @@ -1,11 +1,20 @@ -import { useFeatures } from 'soapbox/hooks'; +import { useFeatures, useLoggedIn } from 'soapbox/hooks'; import { useTimelineStream } from './useTimelineStream'; function useNostrStream() { const features = useFeatures(); - const enabled = features.nostrSign && Boolean(window.nostr); - return useTimelineStream('nostr', 'nostr', null, null, { enabled }); + const { isLoggedIn } = useLoggedIn(); + + return useTimelineStream( + 'nostr', + 'nostr', + null, + null, + { + enabled: isLoggedIn && features.nostrSign && Boolean(window.nostr), + }, + ); } export { useNostrStream }; \ No newline at end of file diff --git a/app/soapbox/api/hooks/streaming/useTimelineStream.ts b/app/soapbox/api/hooks/streaming/useTimelineStream.ts index a0dfb2ded..28998e090 100644 --- a/app/soapbox/api/hooks/streaming/useTimelineStream.ts +++ b/app/soapbox/api/hooks/streaming/useTimelineStream.ts @@ -17,7 +17,7 @@ function useTimelineStream(...args: Parameters) { const streamingUrl = instance.urls.get('streaming_api'); const connect = () => { - if (enabled && accessToken && streamingUrl && !stream.current) { + if (enabled && streamingUrl && !stream.current) { stream.current = dispatch(connectTimelineStream(...args)); } }; diff --git a/app/soapbox/api/hooks/streaming/useUserStream.ts b/app/soapbox/api/hooks/streaming/useUserStream.ts index 5e0bb9aed..cededf2aa 100644 --- a/app/soapbox/api/hooks/streaming/useUserStream.ts +++ b/app/soapbox/api/hooks/streaming/useUserStream.ts @@ -2,14 +2,23 @@ import { fetchAnnouncements } from 'soapbox/actions/announcements'; import { expandNotifications } from 'soapbox/actions/notifications'; import { expandHomeTimeline } from 'soapbox/actions/timelines'; import { useStatContext } from 'soapbox/contexts/stat-context'; +import { useLoggedIn } from 'soapbox/hooks'; import { useTimelineStream } from './useTimelineStream'; import type { AppDispatch } from 'soapbox/store'; function useUserStream() { + const { isLoggedIn } = useLoggedIn(); const statContext = useStatContext(); - return useTimelineStream('home', 'user', refresh, null, { statContext }); + + return useTimelineStream( + 'home', + 'user', + refresh, + null, + { statContext, enabled: isLoggedIn }, + ); } /** Refresh home timeline and notifications. */ From 26dfcb728bc40de09b23aa5ace8b54a9fe377098 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 22 Jul 2023 15:45:02 -0500 Subject: [PATCH 11/11] yarn i18n --- app/soapbox/locales/en.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/soapbox/locales/en.json b/app/soapbox/locales/en.json index bfd15580a..b2d0469e9 100644 --- a/app/soapbox/locales/en.json +++ b/app/soapbox/locales/en.json @@ -868,9 +868,6 @@ "groups.search.placeholder": "Search My Groups", "groups.suggested.label": "Suggested Groups", "groups.tags.title": "Browse Topics", - "hashtag.column_header.tag_mode.all": "and {additional}", - "hashtag.column_header.tag_mode.any": "or {additional}", - "hashtag.column_header.tag_mode.none": "without {additional}", "hashtag.follow": "Follow hashtag", "header.home.label": "Home", "header.login.email.placeholder": "E-mail address",