Add StatContext to store global stat state

This commit is contained in:
Justin 2022-09-27 16:05:19 -04:00
parent 058d0cec0b
commit 002fef27a3
6 changed files with 82 additions and 41 deletions

View file

@ -3,6 +3,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { getSettings } from 'soapbox/actions/settings';
import DropdownMenu from 'soapbox/containers/dropdown_menu_container';
import { useStatContext } from 'soapbox/contexts/stat-context';
import ComposeButton from 'soapbox/features/ui/components/compose-button';
import { useAppSelector, useOwnAccount } from 'soapbox/hooks';
import { getFeatures } from 'soapbox/utils/features';
@ -24,12 +25,12 @@ const messages = defineMessages({
/** Desktop sidebar with links to different views in the app. */
const SidebarNavigation = () => {
const intl = useIntl();
const { unreadChatsCount } = useStatContext();
const instance = useAppSelector((state) => state.instance);
const settings = useAppSelector((state) => getSettings(state));
const account = useOwnAccount();
const notificationCount = useAppSelector((state) => state.notifications.get('unread'));
const chatsCount = useAppSelector((state) => state.chats.items.reduce((acc, curr) => acc + Math.min(curr.unread || 0, 1), 0));
const followRequestsCount = useAppSelector((state) => state.user_lists.follow_requests.items.count());
const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count());
@ -114,7 +115,7 @@ const SidebarNavigation = () => {
<SidebarNavigationLink
to='/chats'
icon={require('@tabler/icons/mail.svg')}
count={chatsCount}
count={unreadChatsCount}
text={<FormattedMessage id='navigation.direct_messages' defaultMessage='Messages' />}
/>
);

View file

@ -2,13 +2,15 @@ import React from 'react';
import { FormattedMessage } from 'react-intl';
import ThumbNavigationLink from 'soapbox/components/thumb_navigation-link';
import { useStatContext } from 'soapbox/contexts/stat-context';
import { useAppSelector, useOwnAccount } from 'soapbox/hooks';
import { getFeatures } from 'soapbox/utils/features';
const ThumbNavigation: React.FC = (): JSX.Element => {
const account = useOwnAccount();
const { unreadChatsCount } = useStatContext();
const notificationCount = useAppSelector((state) => state.notifications.unread);
const chatsCount = useAppSelector((state) => state.chats.items.reduce((acc, curr) => acc + Math.min(curr.unread || 0, 1), 0));
const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count());
const features = getFeatures(useAppSelector((state) => state.instance));
@ -21,7 +23,7 @@ const ThumbNavigation: React.FC = (): JSX.Element => {
text={<FormattedMessage id='navigation.direct_messages' defaultMessage='Messages' />}
to='/chats'
exact
count={chatsCount}
count={unreadChatsCount}
/>
);
}

View file

@ -0,0 +1,29 @@
import React, { createContext, useContext, useMemo, useState } from 'react';
type IStatContext = {
unreadChatsCount: number,
setUnreadChatsCount: React.Dispatch<React.SetStateAction<number>>
}
const StatContext = createContext<any>({
unreadChatsCount: 0,
});
const StatProvider: React.FC = ({ children }) => {
const [unreadChatsCount, setUnreadChatsCount] = useState<number>(0);
const value = useMemo(() => ({
unreadChatsCount,
setUnreadChatsCount,
}), [unreadChatsCount]);
return (
<StatContext.Provider value={value}>
{children}
</StatContext.Provider>
);
};
const useStatContext = (): IStatContext => useContext(StatContext);
export { StatProvider, useStatContext };

View file

@ -3,6 +3,7 @@ import React, { useState } from 'react';
import { Stack } from 'soapbox/components/ui';
import { useChatContext } from 'soapbox/contexts/chat-context';
import { useStatContext } from 'soapbox/contexts/stat-context';
import { useDebounce, useFeatures } from 'soapbox/hooks';
import { IChat, useChats } from 'soapbox/queries/chats';
@ -19,6 +20,7 @@ import Blankslate from './blankslate';
const ChatPane = () => {
const features = useFeatures();
const debounce = useDebounce;
const { unreadChatsCount } = useStatContext();
const [value, setValue] = useState<string>();
const debouncedValue = debounce(value as string, 300);
@ -26,8 +28,6 @@ const ChatPane = () => {
const { chat, setChat, isOpen, isSearching, setSearching, toggleChatPane } = useChatContext();
const { chatsQuery: { data: chats, isLoading } } = useChats(debouncedValue);
const unreadCount = sumBy(chats, (chat) => chat.unread);
const hasSearchValue = Number(debouncedValue?.length) > 0;
const handleClickChat = (chat: IChat) => {
@ -89,7 +89,7 @@ const ChatPane = () => {
<Pane isOpen={isOpen} index={0} main>
<ChatPaneHeader
title='Messages'
unreadCount={unreadCount}
unreadCount={unreadChatsCount}
isOpen={isOpen}
onToggle={toggleChatPane}
secondaryAction={() => {

View file

@ -118,6 +118,7 @@ import { WrappedRoute } from './util/react_router_helpers';
// Dummy import, to make sure that <Status /> ends up in the application bundle.
// Without this it ends up in ~8 very commonly used bundles.
import 'soapbox/components/status';
import { StatProvider } from '../../contexts/stat-context';
const EmptyPage = HomePage;
@ -651,52 +652,54 @@ const UI: React.FC = ({ children }) => {
};
return (
<HotKeys keyMap={keyMap} handlers={me ? handlers : undefined} ref={setHotkeysRef} attach={window} focused>
<div ref={node} style={style}>
<BackgroundShapes />
<StatProvider>
<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}>
{me && (
<BundleContainer fetchComponent={SidebarMenu}>
{Component => <Component />}
</BundleContainer>
)}
{me && features.chats && !mobile && (
<BundleContainer fetchComponent={ChatWidget}>
{Component => <Component />}
</BundleContainer>
)}
<ThumbNavigation />
<BundleContainer fetchComponent={ProfileHoverCard}>
{Component => <Component />}
</BundleContainer>
)}
{me && features.chats && !mobile && (
<BundleContainer fetchComponent={ChatWidget}>
<BundleContainer fetchComponent={StatusHoverCard}>
{Component => <Component />}
</BundleContainer>
)}
<ThumbNavigation />
<BundleContainer fetchComponent={ProfileHoverCard}>
{Component => <Component />}
</BundleContainer>
<BundleContainer fetchComponent={StatusHoverCard}>
{Component => <Component />}
</BundleContainer>
</div>
</div>
</div>
</HotKeys>
</HotKeys>
</StatProvider>
);
};

View file

@ -1,4 +1,5 @@
import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
import sumBy from 'lodash/sumBy';
import { useState } from 'react';
import { fetchRelationships } from 'soapbox/actions/accounts';
@ -6,6 +7,7 @@ import snackbar from 'soapbox/actions/snackbar';
import { getNextLink } from 'soapbox/api';
import compareId from 'soapbox/compare_id';
import { useChatContext } from 'soapbox/contexts/chat-context';
import { useStatContext } from 'soapbox/contexts/stat-context';
import { useApi, useAppDispatch, useFeatures } from 'soapbox/hooks';
import { normalizeChatMessage } from 'soapbox/normalizers';
import { flattenPages, PaginatedResult, updatePageItem } from 'soapbox/utils/queries';
@ -101,6 +103,7 @@ const useChats = (search?: string) => {
const api = useApi();
const dispatch = useAppDispatch();
const features = useFeatures();
const { setUnreadChatsCount } = useStatContext();
const getChats = async (pageParam?: any): Promise<PaginatedResult<IChat>> => {
const endpoint = features.chatsV2 ? '/api/v2/pleroma/chats' : '/api/v1/pleroma/chats';
@ -116,6 +119,9 @@ const useChats = (search?: string) => {
const link = getNextLink(response);
const hasMore = !!link;
// TODO: change to response header
setUnreadChatsCount(sumBy(data, (chat) => chat.unread));
// Set the relationships to these users in the redux store.
dispatch(fetchRelationships(data.map((item) => item.account.id)));