Add LatestAccountsPanel
This commit is contained in:
parent
5f95348ac0
commit
6766892696
|
@ -0,0 +1,57 @@
|
||||||
|
import xIcon from '@tabler/icons/outline/x.svg';
|
||||||
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import Widget from 'soapbox/components/ui/widget.tsx';
|
||||||
|
import AccountContainer from 'soapbox/containers/account-container.tsx';
|
||||||
|
import PlaceholderSidebarSuggestions from 'soapbox/features/placeholder/components/placeholder-sidebar-suggestions.tsx';
|
||||||
|
import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts';
|
||||||
|
import { useDismissSuggestion, useLocalSuggestions } from 'soapbox/queries/suggestions.ts';
|
||||||
|
|
||||||
|
import type { Account as AccountEntity } from 'soapbox/types/entities.ts';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
|
||||||
|
});
|
||||||
|
|
||||||
|
interface ILatestAccountsPanel {
|
||||||
|
limit: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LatestAccountsPanel: React.FC<ILatestAccountsPanel> = ({ limit }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const { account } = useOwnAccount();
|
||||||
|
const { data: suggestions, isFetching } = useLocalSuggestions();
|
||||||
|
const dismissSuggestion = useDismissSuggestion();
|
||||||
|
|
||||||
|
const suggestionsToRender = suggestions.slice(0, limit);
|
||||||
|
|
||||||
|
const handleDismiss = (account: AccountEntity) => {
|
||||||
|
dismissSuggestion.mutate(account.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isFetching && !suggestions.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Widget title={<FormattedMessage id='latest_accounts.title' defaultMessage='Latest Accounts' />}>
|
||||||
|
{isFetching ? (
|
||||||
|
<PlaceholderSidebarSuggestions limit={limit} />
|
||||||
|
) : (
|
||||||
|
suggestionsToRender.map((suggestion: any) => (
|
||||||
|
<AccountContainer
|
||||||
|
key={suggestion.account}
|
||||||
|
// @ts-ignore: TS thinks `id` is passed to <Account>, but it isn't
|
||||||
|
id={suggestion.account}
|
||||||
|
actionIcon={xIcon}
|
||||||
|
actionTitle={intl.formatMessage(messages.dismissSuggestion)}
|
||||||
|
onActionClick={account ? handleDismiss : undefined}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LatestAccountsPanel;
|
|
@ -93,7 +93,7 @@ export const ProfileFieldsPanel = lazy(() => import('soapbox/features/ui/compone
|
||||||
export const PinnedAccountsPanel = lazy(() => import('soapbox/features/ui/components/pinned-accounts-panel.tsx'));
|
export const PinnedAccountsPanel = lazy(() => import('soapbox/features/ui/components/pinned-accounts-panel.tsx'));
|
||||||
export const InstanceInfoPanel = lazy(() => import('soapbox/features/ui/components/instance-info-panel.tsx'));
|
export const InstanceInfoPanel = lazy(() => import('soapbox/features/ui/components/instance-info-panel.tsx'));
|
||||||
export const InstanceModerationPanel = lazy(() => import('soapbox/features/ui/components/instance-moderation-panel.tsx'));
|
export const InstanceModerationPanel = lazy(() => import('soapbox/features/ui/components/instance-moderation-panel.tsx'));
|
||||||
export const LatestAccountsPanel = lazy(() => import('soapbox/features/admin/components/latest-accounts-panel.tsx'));
|
export const LatestAdminAccountsPanel = lazy(() => import('soapbox/features/admin/components/latest-accounts-panel.tsx'));
|
||||||
export const SidebarMenu = lazy(() => import('soapbox/components/sidebar-menu.tsx'));
|
export const SidebarMenu = lazy(() => import('soapbox/components/sidebar-menu.tsx'));
|
||||||
export const ModalContainer = lazy(() => import('soapbox/features/ui/containers/modal-container.ts'));
|
export const ModalContainer = lazy(() => import('soapbox/features/ui/containers/modal-container.ts'));
|
||||||
export const ProfileHoverCard = lazy(() => import('soapbox/components/profile-hover-card.tsx'));
|
export const ProfileHoverCard = lazy(() => import('soapbox/components/profile-hover-card.tsx'));
|
||||||
|
@ -109,6 +109,7 @@ export const FederationRestrictions = lazy(() => import('soapbox/features/federa
|
||||||
export const Aliases = lazy(() => import('soapbox/features/aliases/index.tsx'));
|
export const Aliases = lazy(() => import('soapbox/features/aliases/index.tsx'));
|
||||||
export const Migration = lazy(() => import('soapbox/features/migration/index.tsx'));
|
export const Migration = lazy(() => import('soapbox/features/migration/index.tsx'));
|
||||||
export const WhoToFollowPanel = lazy(() => import('soapbox/features/ui/components/who-to-follow-panel.tsx'));
|
export const WhoToFollowPanel = lazy(() => import('soapbox/features/ui/components/who-to-follow-panel.tsx'));
|
||||||
|
export const LatestAccountsPanel = lazy(() => import('soapbox/features/ui/components/latest-accounts-panel.tsx'));
|
||||||
export const FollowRecommendations = lazy(() => import('soapbox/features/follow-recommendations/index.tsx'));
|
export const FollowRecommendations = lazy(() => import('soapbox/features/follow-recommendations/index.tsx'));
|
||||||
export const Directory = lazy(() => import('soapbox/features/directory/index.tsx'));
|
export const Directory = lazy(() => import('soapbox/features/directory/index.tsx'));
|
||||||
export const RegisterInvite = lazy(() => import('soapbox/features/register-invite/index.tsx'));
|
export const RegisterInvite = lazy(() => import('soapbox/features/register-invite/index.tsx'));
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import Layout from 'soapbox/components/ui/layout.tsx';
|
import Layout from 'soapbox/components/ui/layout.tsx';
|
||||||
import {
|
import { LatestAdminAccountsPanel } from 'soapbox/features/ui/util/async-components.ts';
|
||||||
LatestAccountsPanel,
|
|
||||||
} from 'soapbox/features/ui/util/async-components.ts';
|
|
||||||
|
|
||||||
import LinkFooter from '../features/ui/components/link-footer.tsx';
|
import LinkFooter from '../features/ui/components/link-footer.tsx';
|
||||||
|
|
||||||
|
@ -17,7 +15,7 @@ const AdminPage: React.FC<IAdminPage> = ({ children }) => {
|
||||||
</Layout.Main>
|
</Layout.Main>
|
||||||
|
|
||||||
<Layout.Aside>
|
<Layout.Aside>
|
||||||
<LatestAccountsPanel limit={5} />
|
<LatestAdminAccountsPanel limit={5} />
|
||||||
<LinkFooter />
|
<LinkFooter />
|
||||||
</Layout.Aside>
|
</Layout.Aside>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
BirthdayPanel,
|
BirthdayPanel,
|
||||||
CtaBanner,
|
CtaBanner,
|
||||||
AnnouncementsPanel,
|
AnnouncementsPanel,
|
||||||
|
LatestAccountsPanel,
|
||||||
} from 'soapbox/features/ui/util/async-components.ts';
|
} from 'soapbox/features/ui/util/async-components.ts';
|
||||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||||
|
@ -62,6 +63,14 @@ const HomePage: React.FC<IHomePage> = ({ children }) => {
|
||||||
const acct = account ? account.acct : '';
|
const acct = account ? account.acct : '';
|
||||||
const avatar = account ? account.avatar : '';
|
const avatar = account ? account.avatar : '';
|
||||||
|
|
||||||
|
const renderSuggestions = () => {
|
||||||
|
if (features.suggestionsLocal && pathname !== '/timeline/global') {
|
||||||
|
return <LatestAccountsPanel limit={3} />;
|
||||||
|
} else if (features.suggestions) {
|
||||||
|
return <WhoToFollowPanel limit={3} />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Layout.Main className={clsx('space-y-0 dark:divide-gray-800')}>
|
<Layout.Main className={clsx('space-y-0 dark:divide-gray-800')}>
|
||||||
|
@ -120,9 +129,7 @@ const HomePage: React.FC<IHomePage> = ({ children }) => {
|
||||||
{features.trends && (
|
{features.trends && (
|
||||||
<TrendsPanel limit={5} />
|
<TrendsPanel limit={5} />
|
||||||
)}
|
)}
|
||||||
{features.suggestions && (
|
{renderSuggestions()}
|
||||||
<WhoToFollowPanel limit={3} />
|
|
||||||
)}
|
|
||||||
{features.birthdays && (
|
{features.birthdays && (
|
||||||
<BirthdayPanel limit={10} />
|
<BirthdayPanel limit={10} />
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -24,6 +24,7 @@ type PageParam = {
|
||||||
|
|
||||||
const SuggestionKeys = {
|
const SuggestionKeys = {
|
||||||
suggestions: ['suggestions'] as const,
|
suggestions: ['suggestions'] as const,
|
||||||
|
localSuggestions: ['suggestions', 'local'] as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
const useSuggestions = () => {
|
const useSuggestions = () => {
|
||||||
|
@ -133,4 +134,53 @@ function useOnboardingSuggestions() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useOnboardingSuggestions, useSuggestions, useDismissSuggestion };
|
const useLocalSuggestions = () => {
|
||||||
|
const api = useApi();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const getLocalSuggestions = async (pageParam: PageParam): Promise<PaginatedResult<Result>> => {
|
||||||
|
const endpoint = pageParam?.link || '/api/v2/ditto/suggestions/local';
|
||||||
|
const response = await api.get(endpoint);
|
||||||
|
const next = response.next();
|
||||||
|
const hasMore = !!next;
|
||||||
|
|
||||||
|
const data: Suggestion[] = await response.json();
|
||||||
|
const accounts = data.map(({ account }) => account);
|
||||||
|
const accountIds = accounts.map((account) => account.id);
|
||||||
|
dispatch(importFetchedAccounts(accounts));
|
||||||
|
dispatch(fetchRelationships(accountIds));
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: data.map(x => ({ ...x, account: x.account.id })),
|
||||||
|
link: next ?? undefined,
|
||||||
|
hasMore,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = useInfiniteQuery({
|
||||||
|
queryKey: SuggestionKeys.localSuggestions,
|
||||||
|
queryFn: ({ pageParam }: any) => getLocalSuggestions(pageParam),
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
|
initialPageParam: { nextLink: undefined },
|
||||||
|
getNextPageParam: (config) => {
|
||||||
|
if (config?.hasMore) {
|
||||||
|
return { nextLink: config?.link };
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const data: any = result.data?.pages.reduce<Suggestion[]>(
|
||||||
|
(prev: any, curr: any) => [...prev, ...curr.result],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: data || [],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export { useOnboardingSuggestions, useSuggestions, useDismissSuggestion, useLocalSuggestions };
|
|
@ -1027,6 +1027,8 @@ const getInstanceFeatures = (instance: InstanceV1 | InstanceV2) => {
|
||||||
features.includes('v2_suggestions'),
|
features.includes('v2_suggestions'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
suggestionsLocal: v.software === DITTO,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supports V2 suggested accounts.
|
* Supports V2 suggested accounts.
|
||||||
* @see GET /api/v2/suggestions
|
* @see GET /api/v2/suggestions
|
||||||
|
|
Loading…
Reference in New Issue