Move StatProvider above UI, increment chats unread counter from streaming events

This commit is contained in:
Alex Gleason 2022-12-07 00:45:10 -06:00
parent d811500812
commit fbd2471dc6
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
5 changed files with 74 additions and 53 deletions

View File

@ -2,7 +2,7 @@ import { getSettings } from 'soapbox/actions/settings';
import messages from 'soapbox/locales/messages';
import { ChatKeys, IChat, isLastMessage } from 'soapbox/queries/chats';
import { queryClient } from 'soapbox/queries/client';
import { updateChatListItem } from 'soapbox/utils/chats';
import { getUnreadChatsCount, updateChatListItem } from 'soapbox/utils/chats';
import { removePageItem } from 'soapbox/utils/queries';
import { play, soundCache } from 'soapbox/utils/sounds';
@ -27,6 +27,7 @@ import {
processTimelineUpdate,
} from './timelines';
import type { IStatContext } from 'soapbox/contexts/stat-context';
import type { AppDispatch, RootState } from 'soapbox/store';
import type { APIEntity, Chat } from 'soapbox/types/entities';
@ -79,11 +80,16 @@ const updateChatQuery = (chat: IChat) => {
queryClient.setQueryData<Chat>(ChatKeys.chat(chat.id), newChat as any);
};
interface StreamOpts {
statContext?: IStatContext,
}
const connectTimelineStream = (
timelineId: string,
path: string,
pollingRefresh: ((dispatch: AppDispatch, done?: () => void) => void) | null = null,
accept: ((status: APIEntity) => boolean) | null = null,
opts?: StreamOpts,
) => connectStream(path, pollingRefresh, (dispatch: AppDispatch, getState: () => RootState) => {
const locale = getLocale(getState());
@ -145,6 +151,9 @@ const connectTimelineStream = (
if (settings.getIn(['chats', 'sound'])) {
play(soundCache.chat);
}
// Increment unread counter
opts?.statContext?.setUnreadChatsCount(getUnreadChatsCount());
}
});
break;
@ -186,8 +195,8 @@ const refreshHomeTimelineAndNotification = (dispatch: AppDispatch, done?: () =>
dispatch(expandNotifications({}, () =>
dispatch(fetchAnnouncements(done))))));
const connectUserStream = () =>
connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
const connectUserStream = (opts?: StreamOpts) =>
connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification, null, opts);
const connectCommunityStream = ({ onlyMedia }: Record<string, any> = {}) =>
connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`);

View File

@ -17,6 +17,7 @@ import * as BuildConfig from 'soapbox/build-config';
import GdprBanner from 'soapbox/components/gdpr-banner';
import Helmet from 'soapbox/components/helmet';
import LoadingScreen from 'soapbox/components/loading-screen';
import { StatProvider } from 'soapbox/contexts/stat-context';
import AuthLayout from 'soapbox/features/auth-layout';
import EmbeddedStatus from 'soapbox/features/embedded-status';
import PublicLayout from 'soapbox/features/public-layout';
@ -296,11 +297,13 @@ const Soapbox: React.FC = () => {
return (
<Provider store={store}>
<QueryClientProvider client={queryClient}>
<SoapboxHead>
<SoapboxLoad>
<SoapboxMount />
</SoapboxLoad>
</SoapboxHead>
<StatProvider>
<SoapboxHead>
<SoapboxLoad>
<SoapboxMount />
</SoapboxLoad>
</SoapboxHead>
</StatProvider>
</QueryClientProvider>
</Provider>
);

View File

@ -26,4 +26,4 @@ const StatProvider: React.FC = ({ children }) => {
const useStatContext = (): IStatContext => useContext(StatContext);
export { StatProvider, useStatContext };
export { StatProvider, useStatContext, IStatContext };

View File

@ -24,7 +24,7 @@ import Icon from 'soapbox/components/icon';
import SidebarNavigation from 'soapbox/components/sidebar-navigation';
import ThumbNavigation from 'soapbox/components/thumb-navigation';
import { Layout } from 'soapbox/components/ui';
import { StatProvider } from 'soapbox/contexts/stat-context';
import { useStatContext } from 'soapbox/contexts/stat-context';
import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures, useInstance } from 'soapbox/hooks';
import AdminPage from 'soapbox/pages/admin-page';
import ChatsPage from 'soapbox/pages/chats-page';
@ -323,6 +323,7 @@ const UI: React.FC = ({ children }) => {
const dispatch = useAppDispatch();
const { data: pendingPolicy } = usePendingPolicy();
const instance = useInstance();
const statContext = useStatContext();
const [draggingOver, setDraggingOver] = useState<boolean>(false);
@ -420,7 +421,7 @@ const UI: React.FC = ({ children }) => {
const connectStreaming = () => {
if (!disconnect.current && accessToken && streamingUrl) {
disconnect.current = dispatch(connectUserStream());
disconnect.current = dispatch(connectUserStream({ statContext }));
}
};
@ -642,58 +643,56 @@ const UI: React.FC = ({ children }) => {
};
return (
<StatProvider>
<HotKeys keyMap={keyMap} handlers={me ? handlers : undefined} ref={setHotkeysRef} attach={window} focused>
<div ref={node} style={style}>
<BackgroundShapes />
<HotKeys keyMap={keyMap} handlers={me ? handlers : undefined} ref={setHotkeysRef} attach={window} focused>
<div ref={node} style={style}>
<BackgroundShapes />
<div className='z-10 flex flex-col'>
<Navbar />
<div className='z-10 flex flex-col'>
<Navbar />
<Layout>
<Layout.Sidebar>
{!standalone && <SidebarNavigation />}
</Layout.Sidebar>
<Layout>
<Layout.Sidebar>
{!standalone && <SidebarNavigation />}
</Layout.Sidebar>
<SwitchingColumnsArea>
{children}
</SwitchingColumnsArea>
</Layout>
<SwitchingColumnsArea>
{children}
</SwitchingColumnsArea>
</Layout>
{me && floatingActionButton}
{me && floatingActionButton}
<BundleContainer fetchComponent={UploadArea}>
{Component => <Component active={draggingOver} onClose={closeUploadModal} />}
</BundleContainer>
<BundleContainer fetchComponent={UploadArea}>
{Component => <Component active={draggingOver} onClose={closeUploadModal} />}
</BundleContainer>
{me && (
<BundleContainer fetchComponent={SidebarMenu}>
{Component => <Component />}
</BundleContainer>
)}
{me && features.chats && (
<BundleContainer fetchComponent={ChatWidget}>
{Component => (
<div className='hidden xl:block'>
<Component />
</div>
)}
</BundleContainer>
)}
<ThumbNavigation />
<BundleContainer fetchComponent={ProfileHoverCard}>
{me && (
<BundleContainer fetchComponent={SidebarMenu}>
{Component => <Component />}
</BundleContainer>
)}
<BundleContainer fetchComponent={StatusHoverCard}>
{Component => <Component />}
{me && features.chats && (
<BundleContainer fetchComponent={ChatWidget}>
{Component => (
<div className='hidden xl:block'>
<Component />
</div>
)}
</BundleContainer>
</div>
)}
<ThumbNavigation />
<BundleContainer fetchComponent={ProfileHoverCard}>
{Component => <Component />}
</BundleContainer>
<BundleContainer fetchComponent={StatusHoverCard}>
{Component => <Component />}
</BundleContainer>
</div>
</HotKeys>
</StatProvider>
</div>
</HotKeys>
);
};

View File

@ -1,4 +1,5 @@
import { InfiniteData } from '@tanstack/react-query';
import sumBy from 'lodash/sumBy';
import { normalizeChatMessage } from 'soapbox/normalizers';
import { ChatKeys } from 'soapbox/queries/chats';
@ -71,4 +72,13 @@ const updateChatListItem = (newChat: ChatPayload) => {
}
};
export { updateChatListItem };
/** Get unread chats count. */
const getUnreadChatsCount = (): number => {
const chats = flattenPages(
queryClient.getQueryData<InfiniteData<PaginatedResult<Chat>>>(ChatKeys.chatSearch()),
);
return sumBy(chats, chat => chat.unread);
};
export { updateChatListItem, getUnreadChatsCount };