pl-fe: migrate suggested accounts to react query
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
781a2430e4
commit
8f8bd724cb
11 changed files with 63 additions and 158 deletions
|
@ -1,6 +1,16 @@
|
|||
import { PLEROMA, type UpdateNotificationSettingsParams, type Account, type CreateAccountParams, type PaginatedResponse, type Relationship, Token, PlApiClient } from 'pl-api';
|
||||
import {
|
||||
PLEROMA,
|
||||
type UpdateNotificationSettingsParams,
|
||||
type Account,
|
||||
type CreateAccountParams,
|
||||
type PaginatedResponse,
|
||||
type PlApiClient,
|
||||
type Relationship,
|
||||
type Token,
|
||||
} from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { selectAccount } from 'pl-fe/selectors';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
|
@ -8,6 +18,7 @@ import { getClient, type PlfeResponse } from '../api';
|
|||
|
||||
import { importEntities } from './importer';
|
||||
|
||||
import type { MinifiedSuggestion } from 'pl-fe/api/hooks/trends/use-suggested-accounts';
|
||||
import type { MinifiedStatus } from 'pl-fe/reducers/statuses';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
import type { History } from 'pl-fe/types/history';
|
||||
|
@ -187,6 +198,11 @@ const blockAccount = (accountId: string) =>
|
|||
return getClient(getState).filtering.blockAccount(accountId)
|
||||
.then(response => {
|
||||
dispatch(importEntities({ relationships: [response] }));
|
||||
|
||||
queryClient.setQueryData<Array<MinifiedSuggestion>>(['suggestions'], suggestions => suggestions
|
||||
? suggestions.filter((suggestion) => suggestion.account_id !== accountId)
|
||||
: undefined);
|
||||
|
||||
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
||||
return dispatch(blockAccountSuccess(response, getState().statuses));
|
||||
}).catch(error => dispatch(blockAccountFail(error)));
|
||||
|
@ -243,6 +259,11 @@ const muteAccount = (accountId: string, notifications?: boolean, duration = 0) =
|
|||
return client.filtering.muteAccount(accountId, params)
|
||||
.then(response => {
|
||||
dispatch(importEntities({ relationships: [response] }));
|
||||
|
||||
queryClient.setQueryData<Array<MinifiedSuggestion>>(['suggestions'], suggestions => suggestions
|
||||
? suggestions.filter((suggestion) => suggestion.account_id !== accountId)
|
||||
: undefined);
|
||||
|
||||
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
||||
return dispatch(muteAccountSuccess(response, getState().statuses));
|
||||
})
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
import { getClient } from '../api';
|
||||
|
||||
import type { PaginatedResponse } from 'pl-api';
|
||||
import type { MinifiedSuggestion } from 'pl-fe/api/hooks/trends/use-suggested-accounts';
|
||||
import type { EntityStore } from 'pl-fe/entity-store/types';
|
||||
import type { Account } from 'pl-fe/normalizers/account';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
@ -35,6 +37,10 @@ const blockDomain = (domain: string) =>
|
|||
const accounts = selectAccountsByDomain(getState(), domain);
|
||||
if (!accounts) return;
|
||||
dispatch(blockDomainSuccess(domain, accounts));
|
||||
|
||||
queryClient.setQueryData<Array<MinifiedSuggestion>>(['suggestions'], suggestions => suggestions
|
||||
? suggestions.filter((suggestion) => !accounts.includes(suggestion.account_id))
|
||||
: undefined);
|
||||
}).catch(err => {
|
||||
dispatch(blockDomainFail(domain, err));
|
||||
});
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
import { getClient } from '../api';
|
||||
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importEntities } from './importer';
|
||||
import { insertSuggestionsIntoTimeline } from './timelines';
|
||||
|
||||
import type { Suggestion } from 'pl-api';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
||||
const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST' as const;
|
||||
const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS' as const;
|
||||
const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL' as const;
|
||||
|
||||
interface SuggestionsFetchRequestAction {
|
||||
type: typeof SUGGESTIONS_FETCH_REQUEST;
|
||||
}
|
||||
|
||||
interface SuggestionsFetchSuccessAction {
|
||||
type: typeof SUGGESTIONS_FETCH_SUCCESS;
|
||||
suggestions: Array<Suggestion>;
|
||||
}
|
||||
|
||||
interface SuggestionsFetchFailAction {
|
||||
type: typeof SUGGESTIONS_FETCH_FAIL;
|
||||
error: any;
|
||||
skipAlert: true;
|
||||
}
|
||||
|
||||
const fetchSuggestions = (limit = 50) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
const client = getClient(state);
|
||||
const me = state.me;
|
||||
|
||||
if (!me) return null;
|
||||
|
||||
if (client.features.suggestions) {
|
||||
dispatch<SuggestionsFetchRequestAction>({ type: SUGGESTIONS_FETCH_REQUEST });
|
||||
|
||||
return getClient(getState).myAccount.getSuggestions(limit).then((suggestions) => {
|
||||
const accounts = suggestions.map(({ account }) => account);
|
||||
|
||||
dispatch(importEntities({ accounts }));
|
||||
dispatch<SuggestionsFetchSuccessAction>({ type: SUGGESTIONS_FETCH_SUCCESS, suggestions });
|
||||
|
||||
dispatch(fetchRelationships(accounts.map(({ id }) => id)));
|
||||
return suggestions;
|
||||
}).catch(error => {
|
||||
dispatch<SuggestionsFetchFailAction>({ type: SUGGESTIONS_FETCH_FAIL, error, skipAlert: true });
|
||||
throw error;
|
||||
});
|
||||
} else {
|
||||
// Do nothing
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchSuggestionsForTimeline = () => (dispatch: AppDispatch) => {
|
||||
dispatch(fetchSuggestions(20))?.then(() => dispatch(insertSuggestionsIntoTimeline()));
|
||||
};
|
||||
|
||||
type SuggestionsAction =
|
||||
| SuggestionsFetchRequestAction
|
||||
| SuggestionsFetchSuccessAction
|
||||
| SuggestionsFetchFailAction;
|
||||
|
||||
export {
|
||||
SUGGESTIONS_FETCH_REQUEST,
|
||||
SUGGESTIONS_FETCH_SUCCESS,
|
||||
SUGGESTIONS_FETCH_FAIL,
|
||||
fetchSuggestions,
|
||||
fetchSuggestionsForTimeline,
|
||||
type SuggestionsAction,
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { importEntities } from 'pl-fe/actions/importer';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
|
||||
import type { Suggestion } from 'pl-api';
|
||||
|
||||
type MinifiedSuggestion = Omit<Suggestion, 'account'> & { account_id: string };
|
||||
|
||||
const useSuggestedAccounts = () => {
|
||||
const client = useClient();
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['suggestions'],
|
||||
queryFn: () => client.myAccount.getSuggestions().then((suggestions) => {
|
||||
dispatch(importEntities({ accounts: suggestions.map(({ account }) => account) }));
|
||||
return suggestions.map(({ account, ...suggestion }): MinifiedSuggestion => ({ account_id: account.id, ...suggestion }));
|
||||
}),
|
||||
enabled: features.suggestions || features.suggestionsV2,
|
||||
});
|
||||
};
|
||||
|
||||
export { useSuggestedAccounts, type MinifiedSuggestion };
|
|
@ -3,12 +3,12 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
|
||||
import { useSuggestedAccounts } from 'pl-fe/api/hooks/trends/use-suggested-accounts';
|
||||
import Card, { CardBody, CardTitle } from 'pl-fe/components/ui/card';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import VerificationBadge from 'pl-fe/components/verification-badge';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
|
||||
import Emojify from '../emoji/emojify';
|
||||
import ActionButton from '../ui/components/action-button';
|
||||
|
@ -69,10 +69,9 @@ interface IFeedSuggesetions {
|
|||
|
||||
const FeedSuggestions: React.FC<IFeedSuggesetions> = ({ statusId, onMoveUp, onMoveDown }) => {
|
||||
const intl = useIntl();
|
||||
const suggestedProfiles = useAppSelector((state) => state.suggestions.items);
|
||||
const isLoading = useAppSelector((state) => state.suggestions.isLoading);
|
||||
const { data: suggestedProfiles, isLoading } = useSuggestedAccounts();
|
||||
|
||||
if (!isLoading && suggestedProfiles.length === 0) return null;
|
||||
if (!isLoading && suggestedProfiles?.length === 0) return null;
|
||||
|
||||
const handleHotkeyMoveUp = (e?: KeyboardEvent): void => {
|
||||
if (onMoveUp) {
|
||||
|
@ -107,7 +106,7 @@ const FeedSuggestions: React.FC<IFeedSuggesetions> = ({ statusId, onMoveUp, onMo
|
|||
|
||||
<CardBody>
|
||||
<HStack space={4} alignItems='center' className='overflow-x-auto md:space-x-0 lg:overflow-x-hidden'>
|
||||
{suggestedProfiles.slice(0, 4).map((suggestedProfile) => (
|
||||
{suggestedProfiles?.slice(0, 4).map((suggestedProfile) => (
|
||||
<SuggestionItem key={suggestedProfile.account_id} accountId={suggestedProfile.account_id} />
|
||||
))}
|
||||
</HStack>
|
||||
|
|
|
@ -5,6 +5,7 @@ import { useSearchParams } from 'react-router-dom-v5-compat';
|
|||
|
||||
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
|
||||
import { useSearchAccounts, useSearchHashtags, useSearchStatuses } from 'pl-fe/api/hooks/search/use-search';
|
||||
import { useSuggestedAccounts } from 'pl-fe/api/hooks/trends/use-suggested-accounts';
|
||||
import { useTrendingLinks } from 'pl-fe/api/hooks/trends/use-trending-links';
|
||||
import { useTrendingStatuses } from 'pl-fe/api/hooks/trends/use-trending-statuses';
|
||||
import Hashtag from 'pl-fe/components/hashtag';
|
||||
|
@ -19,7 +20,6 @@ import StatusContainer from 'pl-fe/containers/status-container';
|
|||
import PlaceholderAccount from 'pl-fe/features/placeholder/components/placeholder-account';
|
||||
import PlaceholderHashtag from 'pl-fe/features/placeholder/components/placeholder-hashtag';
|
||||
import PlaceholderStatus from 'pl-fe/features/placeholder/components/placeholder-status';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import useTrends from 'pl-fe/queries/trends';
|
||||
|
||||
|
@ -65,7 +65,7 @@ const SearchResults = () => {
|
|||
else setParams(params => ({ ...Object.fromEntries(params.entries()), type: newActiveFilter }));
|
||||
};
|
||||
|
||||
const suggestions = useAppSelector((state) => state.suggestions.items);
|
||||
const { data: suggestions } = useSuggestedAccounts();
|
||||
const { data: trendingTags } = useTrends();
|
||||
const { data: trendingStatuses } = useTrendingStatuses();
|
||||
const { data: trendingLinks } = useTrendingLinks();
|
||||
|
|
|
@ -11,7 +11,6 @@ import { fetchMarker } from 'pl-fe/actions/markers';
|
|||
import { expandNotifications } from 'pl-fe/actions/notifications';
|
||||
import { register as registerPushNotifications } from 'pl-fe/actions/push-notifications';
|
||||
import { fetchScheduledStatuses } from 'pl-fe/actions/scheduled-statuses';
|
||||
import { fetchSuggestionsForTimeline } from 'pl-fe/actions/suggestions';
|
||||
import { fetchHomeTimeline } from 'pl-fe/actions/timelines';
|
||||
import { useUserStream } from 'pl-fe/api/hooks/streaming/use-user-stream';
|
||||
import SidebarNavigation from 'pl-fe/components/sidebar-navigation';
|
||||
|
@ -388,9 +387,7 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|||
|
||||
dispatch(fetchDraftStatuses());
|
||||
|
||||
dispatch(fetchHomeTimeline(false, () => {
|
||||
dispatch(fetchSuggestionsForTimeline());
|
||||
}));
|
||||
dispatch(fetchHomeTimeline());
|
||||
|
||||
dispatch(expandNotifications())
|
||||
// @ts-ignore
|
||||
|
|
|
@ -35,7 +35,6 @@ import scheduled_statuses from './scheduled-statuses';
|
|||
import security from './security';
|
||||
import status_lists from './status-lists';
|
||||
import statuses from './statuses';
|
||||
import suggestions from './suggestions';
|
||||
import tags from './tags';
|
||||
import timelines from './timelines';
|
||||
import trending_statuses from './trending-statuses';
|
||||
|
@ -74,7 +73,6 @@ const reducers = {
|
|||
security,
|
||||
status_lists,
|
||||
statuses,
|
||||
suggestions,
|
||||
tags,
|
||||
timelines,
|
||||
trending_statuses,
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import reducer from './suggestions';
|
||||
|
||||
describe('suggestions reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {} as any).toJS()).toEqual({
|
||||
items: [],
|
||||
next: null,
|
||||
isLoading: false,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,58 +0,0 @@
|
|||
import { Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS, type AccountsAction } from 'pl-fe/actions/accounts';
|
||||
import { DOMAIN_BLOCK_SUCCESS, type DomainBlocksAction } from 'pl-fe/actions/domain-blocks';
|
||||
import {
|
||||
SUGGESTIONS_FETCH_REQUEST,
|
||||
SUGGESTIONS_FETCH_SUCCESS,
|
||||
SUGGESTIONS_FETCH_FAIL,
|
||||
type SuggestionsAction,
|
||||
} from 'pl-fe/actions/suggestions';
|
||||
|
||||
import type { Suggestion as SuggestionEntity } from 'pl-api';
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
items: Array<MinifiedSuggestion>(),
|
||||
isLoading: false,
|
||||
});
|
||||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
|
||||
const minifySuggestion = ({ account, ...suggestion }: SuggestionEntity) => ({
|
||||
...suggestion,
|
||||
account_id: account.id,
|
||||
});
|
||||
|
||||
type MinifiedSuggestion = ReturnType<typeof minifySuggestion>;
|
||||
|
||||
const importSuggestions = (state: State, suggestions: SuggestionEntity[]) =>
|
||||
state.withMutations(state => {
|
||||
state.update('items', items => items.concat(suggestions.map(minifySuggestion)));
|
||||
state.set('isLoading', false);
|
||||
});
|
||||
|
||||
const dismissAccount = (state: State, accountId: string) =>
|
||||
state.update('items', items => items.filter(item => item.account_id !== accountId));
|
||||
|
||||
const dismissAccounts = (state: State, accountIds: string[]) =>
|
||||
state.update('items', items => items.filter(item => !accountIds.includes(item.account_id)));
|
||||
|
||||
const suggestionsReducer = (state: State = ReducerRecord(), action: AccountsAction | DomainBlocksAction | SuggestionsAction) => {
|
||||
switch (action.type) {
|
||||
case SUGGESTIONS_FETCH_REQUEST:
|
||||
return state.set('isLoading', true);
|
||||
case SUGGESTIONS_FETCH_SUCCESS:
|
||||
return importSuggestions(state, action.suggestions);
|
||||
case SUGGESTIONS_FETCH_FAIL:
|
||||
return state.set('isLoading', false);
|
||||
case ACCOUNT_BLOCK_SUCCESS:
|
||||
case ACCOUNT_MUTE_SUCCESS:
|
||||
return dismissAccount(state, action.relationship.id);
|
||||
case DOMAIN_BLOCK_SUCCESS:
|
||||
return dismissAccounts(state, action.accounts);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export { suggestionsReducer as default };
|
|
@ -203,7 +203,7 @@ const userLists = (state = initialState, action: AccountsAction | DirectoryActio
|
|||
case PINNED_ACCOUNTS_FETCH_SUCCESS:
|
||||
return normalizeList(state, ['pinned', action.accountId], action.accounts, action.next);
|
||||
case BIRTHDAY_REMINDERS_FETCH_SUCCESS:
|
||||
return normalizeList(state, ['birthday_reminders', action.accountId], action.accounts, action.next);
|
||||
return normalizeList(state, ['birthday_reminders', action.accountId], action.accounts);
|
||||
case FAMILIAR_FOLLOWERS_FETCH_SUCCESS:
|
||||
return normalizeList(state, ['familiar_followers', action.accountId], action.accounts, action.next);
|
||||
case EVENT_PARTICIPATIONS_FETCH_SUCCESS:
|
||||
|
|
Loading…
Reference in a new issue