pl-fe: migrate follow requests to tanstack query
Signed-off-by: mkljczk <git@mkljczk.pl>
This commit is contained in:
parent
0218fa445f
commit
f6e2f7c6c6
12 changed files with 103 additions and 499 deletions
|
@ -7,7 +7,6 @@ import { normalizeAccount } from 'pl-fe/normalizers/account';
|
|||
import { ListRecord, ReducerRecord } from 'pl-fe/reducers/user-lists';
|
||||
|
||||
import {
|
||||
authorizeFollowRequest,
|
||||
blockAccount,
|
||||
createAccount,
|
||||
expandFollowRequests,
|
||||
|
@ -940,240 +939,3 @@ describe('fetchRelationships()', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchFollowRequests()', () => {
|
||||
describe('when logged out', () => {
|
||||
beforeEach(() => {
|
||||
const state = { ...rootState, me: null };
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
||||
it('should do nothing', async() => {
|
||||
await store.dispatch(fetchFollowRequests());
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when logged in', () => {
|
||||
beforeEach(() => {
|
||||
const state = { ...rootState, me: '123' };
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
||||
describe('with a successful API request', () => {
|
||||
beforeEach(() => {
|
||||
const state = {
|
||||
...rootState,
|
||||
me: '123',
|
||||
relationships: ImmutableMap(),
|
||||
};
|
||||
|
||||
store = mockStore(state);
|
||||
|
||||
__stub((mock) => {
|
||||
mock.onGet('/api/v1/follow_requests').reply(200, [], {
|
||||
link: '<https://example.com/api/v1/follow_requests?since_id=1>; rel=\'prev\'',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch the correct actions', async() => {
|
||||
const expectedActions = [
|
||||
{ type: 'FOLLOW_REQUESTS_FETCH_REQUEST' },
|
||||
{ type: 'ACCOUNTS_IMPORT', accounts: [] },
|
||||
{
|
||||
type: 'FOLLOW_REQUESTS_FETCH_SUCCESS',
|
||||
accounts: [],
|
||||
next: null,
|
||||
},
|
||||
];
|
||||
await store.dispatch(fetchFollowRequests());
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with an unsuccessful API request', () => {
|
||||
beforeEach(() => {
|
||||
__stub((mock) => {
|
||||
mock.onGet('/api/v1/follow_requests').networkError();
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch the correct actions', async() => {
|
||||
const expectedActions = [
|
||||
{ type: 'FOLLOW_REQUESTS_FETCH_REQUEST' },
|
||||
{ type: 'FOLLOW_REQUESTS_FETCH_FAIL', error: new Error('Network Error') },
|
||||
];
|
||||
await store.dispatch(fetchFollowRequests());
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('expandFollowRequests()', () => {
|
||||
describe('when logged out', () => {
|
||||
beforeEach(() => {
|
||||
const state = { ...rootState, me: null };
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
||||
it('should do nothing', async() => {
|
||||
await store.dispatch(expandFollowRequests());
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when logged in', () => {
|
||||
beforeEach(() => {
|
||||
const state = {
|
||||
...rootState,
|
||||
me: '123',
|
||||
user_lists: ReducerRecord({
|
||||
follow_requests: ListRecord({
|
||||
next: 'next_url',
|
||||
}),
|
||||
}),
|
||||
};
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
||||
describe('when the url is null', () => {
|
||||
beforeEach(() => {
|
||||
const state = {
|
||||
...rootState,
|
||||
me: '123',
|
||||
user_lists: ReducerRecord({
|
||||
follow_requests: ListRecord({
|
||||
next: null,
|
||||
}),
|
||||
}),
|
||||
};
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
||||
it('should do nothing', async() => {
|
||||
await store.dispatch(expandFollowRequests());
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a successful API request', () => {
|
||||
beforeEach(() => {
|
||||
__stub((mock) => {
|
||||
mock.onGet('next_url').reply(200, [], {
|
||||
link: '<next_url>; rel=\'prev\'',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch the correct actions', async() => {
|
||||
const expectedActions = [
|
||||
{ type: 'FOLLOW_REQUESTS_EXPAND_REQUEST' },
|
||||
{ type: 'ACCOUNTS_IMPORT', accounts: [] },
|
||||
{
|
||||
type: 'FOLLOW_REQUESTS_EXPAND_SUCCESS',
|
||||
accounts: [],
|
||||
next: null,
|
||||
},
|
||||
];
|
||||
await store.dispatch(expandFollowRequests());
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with an unsuccessful API request', () => {
|
||||
beforeEach(() => {
|
||||
__stub((mock) => {
|
||||
mock.onGet('next_url').networkError();
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch the correct actions', async() => {
|
||||
const expectedActions = [
|
||||
{ type: 'FOLLOW_REQUESTS_EXPAND_REQUEST' },
|
||||
{ type: 'FOLLOW_REQUESTS_EXPAND_FAIL', error: new Error('Network Error') },
|
||||
];
|
||||
await store.dispatch(expandFollowRequests());
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('authorizeFollowRequest()', () => {
|
||||
const id = '1';
|
||||
|
||||
describe('when logged out', () => {
|
||||
beforeEach(() => {
|
||||
const state = { ...rootState, me: null };
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
||||
it('should do nothing', async() => {
|
||||
await store.dispatch(authorizeFollowRequest(id));
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when logged in', () => {
|
||||
beforeEach(() => {
|
||||
const state = { ...rootState, me: '123' };
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
||||
describe('with a successful API request', () => {
|
||||
beforeEach(() => {
|
||||
__stub((mock) => {
|
||||
mock.onPost(`/api/v1/follow_requests/${id}/authorize`).reply(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch the correct actions', async() => {
|
||||
const expectedActions = [
|
||||
{ type: 'FOLLOW_REQUEST_AUTHORIZE_REQUEST', id },
|
||||
{ type: 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS', id },
|
||||
];
|
||||
await store.dispatch(authorizeFollowRequest(id));
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with an unsuccessful API request', () => {
|
||||
beforeEach(() => {
|
||||
__stub((mock) => {
|
||||
mock.onPost(`/api/v1/follow_requests/${id}/authorize`).networkError();
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch the correct actions', async() => {
|
||||
const expectedActions = [
|
||||
{ type: 'FOLLOW_REQUEST_AUTHORIZE_REQUEST', id },
|
||||
{ type: 'FOLLOW_REQUEST_AUTHORIZE_FAIL', id, error: new Error('Network Error') },
|
||||
];
|
||||
await store.dispatch(authorizeFollowRequest(id));
|
||||
const actions = store.getActions();
|
||||
|
||||
expect(actions).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,6 @@ import {
|
|||
type UpdateNotificationSettingsParams,
|
||||
type Account,
|
||||
type CreateAccountParams,
|
||||
type PaginatedResponse,
|
||||
type PlApiClient,
|
||||
type Relationship,
|
||||
type Token,
|
||||
|
@ -51,22 +50,6 @@ const ACCOUNT_LOOKUP_REQUEST = 'ACCOUNT_LOOKUP_REQUEST' as const;
|
|||
const ACCOUNT_LOOKUP_SUCCESS = 'ACCOUNT_LOOKUP_SUCCESS' as const;
|
||||
const ACCOUNT_LOOKUP_FAIL = 'ACCOUNT_LOOKUP_FAIL' as const;
|
||||
|
||||
const FOLLOW_REQUESTS_FETCH_REQUEST = 'FOLLOW_REQUESTS_FETCH_REQUEST' as const;
|
||||
const FOLLOW_REQUESTS_FETCH_SUCCESS = 'FOLLOW_REQUESTS_FETCH_SUCCESS' as const;
|
||||
const FOLLOW_REQUESTS_FETCH_FAIL = 'FOLLOW_REQUESTS_FETCH_FAIL' as const;
|
||||
|
||||
const FOLLOW_REQUESTS_EXPAND_REQUEST = 'FOLLOW_REQUESTS_EXPAND_REQUEST' as const;
|
||||
const FOLLOW_REQUESTS_EXPAND_SUCCESS = 'FOLLOW_REQUESTS_EXPAND_SUCCESS' as const;
|
||||
const FOLLOW_REQUESTS_EXPAND_FAIL = 'FOLLOW_REQUESTS_EXPAND_FAIL' as const;
|
||||
|
||||
const FOLLOW_REQUEST_AUTHORIZE_REQUEST = 'FOLLOW_REQUEST_AUTHORIZE_REQUEST' as const;
|
||||
const FOLLOW_REQUEST_AUTHORIZE_SUCCESS = 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS' as const;
|
||||
const FOLLOW_REQUEST_AUTHORIZE_FAIL = 'FOLLOW_REQUEST_AUTHORIZE_FAIL' as const;
|
||||
|
||||
const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST' as const;
|
||||
const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS' as const;
|
||||
const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL' as const;
|
||||
|
||||
const NOTIFICATION_SETTINGS_REQUEST = 'NOTIFICATION_SETTINGS_REQUEST' as const;
|
||||
const NOTIFICATION_SETTINGS_SUCCESS = 'NOTIFICATION_SETTINGS_SUCCESS' as const;
|
||||
const NOTIFICATION_SETTINGS_FAIL = 'NOTIFICATION_SETTINGS_FAIL' as const;
|
||||
|
@ -313,120 +296,6 @@ const fetchRelationships = (accountIds: string[]) =>
|
|||
.then(response => dispatch(importEntities({ relationships: response })));
|
||||
};
|
||||
|
||||
const fetchFollowRequests = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return null;
|
||||
|
||||
dispatch(fetchFollowRequestsRequest());
|
||||
|
||||
return getClient(getState()).myAccount.getFollowRequests()
|
||||
.then(response => {
|
||||
dispatch(importEntities({ accounts: response.items }));
|
||||
dispatch(fetchFollowRequestsSuccess(response.items, response.next));
|
||||
})
|
||||
.catch(error => dispatch(fetchFollowRequestsFail(error)));
|
||||
};
|
||||
|
||||
const fetchFollowRequestsRequest = () => ({
|
||||
type: FOLLOW_REQUESTS_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
const fetchFollowRequestsSuccess = (accounts: Array<Account>, next: (() => Promise<PaginatedResponse<Account>>) | null) => ({
|
||||
type: FOLLOW_REQUESTS_FETCH_SUCCESS,
|
||||
accounts,
|
||||
next,
|
||||
});
|
||||
|
||||
const fetchFollowRequestsFail = (error: unknown) => ({
|
||||
type: FOLLOW_REQUESTS_FETCH_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
const expandFollowRequests = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return null;
|
||||
|
||||
const next = getState().user_lists.follow_requests.next;
|
||||
|
||||
if (next === null) return null;
|
||||
|
||||
dispatch(expandFollowRequestsRequest());
|
||||
|
||||
return next().then(response => {
|
||||
dispatch(importEntities({ accounts: response.items }));
|
||||
dispatch(expandFollowRequestsSuccess(response.items, response.next));
|
||||
}).catch(error => dispatch(expandFollowRequestsFail(error)));
|
||||
};
|
||||
|
||||
const expandFollowRequestsRequest = () => ({
|
||||
type: FOLLOW_REQUESTS_EXPAND_REQUEST,
|
||||
});
|
||||
|
||||
const expandFollowRequestsSuccess = (accounts: Array<Account>, next: (() => Promise<PaginatedResponse<Account>>) | null) => ({
|
||||
type: FOLLOW_REQUESTS_EXPAND_SUCCESS,
|
||||
accounts,
|
||||
next,
|
||||
});
|
||||
|
||||
const expandFollowRequestsFail = (error: unknown) => ({
|
||||
type: FOLLOW_REQUESTS_EXPAND_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
const authorizeFollowRequest = (accountId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return null;
|
||||
|
||||
dispatch(authorizeFollowRequestRequest(accountId));
|
||||
|
||||
return getClient(getState()).myAccount.acceptFollowRequest(accountId)
|
||||
.then(() => dispatch(authorizeFollowRequestSuccess(accountId)))
|
||||
.catch(error => dispatch(authorizeFollowRequestFail(accountId, error)));
|
||||
};
|
||||
|
||||
const authorizeFollowRequestRequest = (accountId: string) => ({
|
||||
type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
|
||||
accountId,
|
||||
});
|
||||
|
||||
const authorizeFollowRequestSuccess = (accountId: string) => ({
|
||||
type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
||||
accountId,
|
||||
});
|
||||
|
||||
const authorizeFollowRequestFail = (accountId: string, error: unknown) => ({
|
||||
type: FOLLOW_REQUEST_AUTHORIZE_FAIL,
|
||||
accountId,
|
||||
error,
|
||||
});
|
||||
|
||||
const rejectFollowRequest = (accountId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
dispatch(rejectFollowRequestRequest(accountId));
|
||||
|
||||
return getClient(getState()).myAccount.rejectFollowRequest(accountId)
|
||||
.then(() => dispatch(rejectFollowRequestSuccess(accountId)))
|
||||
.catch(error => dispatch(rejectFollowRequestFail(accountId, error)));
|
||||
};
|
||||
|
||||
const rejectFollowRequestRequest = (accountId: string) => ({
|
||||
type: FOLLOW_REQUEST_REJECT_REQUEST,
|
||||
accountId,
|
||||
});
|
||||
|
||||
const rejectFollowRequestSuccess = (accountId: string) => ({
|
||||
type: FOLLOW_REQUEST_REJECT_SUCCESS,
|
||||
accountId,
|
||||
});
|
||||
|
||||
const rejectFollowRequestFail = (accountId: string, error: unknown) => ({
|
||||
type: FOLLOW_REQUEST_REJECT_FAIL,
|
||||
accountId,
|
||||
error,
|
||||
});
|
||||
|
||||
const pinAccount = (accountId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return dispatch(noOp);
|
||||
|
@ -580,18 +449,6 @@ type AccountsAction =
|
|||
| ReturnType<typeof muteAccountRequest>
|
||||
| ReturnType<typeof muteAccountSuccess>
|
||||
| ReturnType<typeof muteAccountFail>
|
||||
| ReturnType<typeof fetchFollowRequestsRequest>
|
||||
| ReturnType<typeof fetchFollowRequestsSuccess>
|
||||
| ReturnType<typeof fetchFollowRequestsFail>
|
||||
| ReturnType<typeof expandFollowRequestsRequest>
|
||||
| ReturnType<typeof expandFollowRequestsSuccess>
|
||||
| ReturnType<typeof expandFollowRequestsFail>
|
||||
| ReturnType<typeof authorizeFollowRequestRequest>
|
||||
| ReturnType<typeof authorizeFollowRequestSuccess>
|
||||
| ReturnType<typeof authorizeFollowRequestFail>
|
||||
| ReturnType<typeof rejectFollowRequestRequest>
|
||||
| ReturnType<typeof rejectFollowRequestSuccess>
|
||||
| ReturnType<typeof rejectFollowRequestFail>
|
||||
| NotificationSettingsRequestAction
|
||||
| NotificationSettingsSuccessAction
|
||||
| NotificationSettingsFailAction
|
||||
|
@ -627,18 +484,6 @@ export {
|
|||
ACCOUNT_LOOKUP_REQUEST,
|
||||
ACCOUNT_LOOKUP_SUCCESS,
|
||||
ACCOUNT_LOOKUP_FAIL,
|
||||
FOLLOW_REQUESTS_FETCH_REQUEST,
|
||||
FOLLOW_REQUESTS_FETCH_SUCCESS,
|
||||
FOLLOW_REQUESTS_FETCH_FAIL,
|
||||
FOLLOW_REQUESTS_EXPAND_REQUEST,
|
||||
FOLLOW_REQUESTS_EXPAND_SUCCESS,
|
||||
FOLLOW_REQUESTS_EXPAND_FAIL,
|
||||
FOLLOW_REQUEST_AUTHORIZE_REQUEST,
|
||||
FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
||||
FOLLOW_REQUEST_AUTHORIZE_FAIL,
|
||||
FOLLOW_REQUEST_REJECT_REQUEST,
|
||||
FOLLOW_REQUEST_REJECT_SUCCESS,
|
||||
FOLLOW_REQUEST_REJECT_FAIL,
|
||||
NOTIFICATION_SETTINGS_REQUEST,
|
||||
NOTIFICATION_SETTINGS_SUCCESS,
|
||||
NOTIFICATION_SETTINGS_FAIL,
|
||||
|
@ -651,10 +496,6 @@ export {
|
|||
unmuteAccount,
|
||||
removeFromFollowers,
|
||||
fetchRelationships,
|
||||
fetchFollowRequests,
|
||||
expandFollowRequests,
|
||||
authorizeFollowRequest,
|
||||
rejectFollowRequest,
|
||||
pinAccount,
|
||||
unpinAccount,
|
||||
updateNotificationSettings,
|
||||
|
|
|
@ -55,12 +55,6 @@ const REMOTE_INTERACTION_REQUEST = 'REMOTE_INTERACTION_REQUEST' as const;
|
|||
const REMOTE_INTERACTION_SUCCESS = 'REMOTE_INTERACTION_SUCCESS' as const;
|
||||
const REMOTE_INTERACTION_FAIL = 'REMOTE_INTERACTION_FAIL' as const;
|
||||
|
||||
const FAVOURITES_EXPAND_SUCCESS = 'FAVOURITES_EXPAND_SUCCESS' as const;
|
||||
const FAVOURITES_EXPAND_FAIL = 'FAVOURITES_EXPAND_FAIL' as const;
|
||||
|
||||
const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS' as const;
|
||||
const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL' as const;
|
||||
|
||||
const noOp = () => new Promise(f => f(undefined));
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -551,10 +545,6 @@ export {
|
|||
REMOTE_INTERACTION_REQUEST,
|
||||
REMOTE_INTERACTION_SUCCESS,
|
||||
REMOTE_INTERACTION_FAIL,
|
||||
FAVOURITES_EXPAND_SUCCESS,
|
||||
FAVOURITES_EXPAND_FAIL,
|
||||
REBLOGS_EXPAND_SUCCESS,
|
||||
REBLOGS_EXPAND_FAIL,
|
||||
reblog,
|
||||
unreblog,
|
||||
toggleReblog,
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'intl-pluralrules';
|
|||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { appendFollowRequest } from 'pl-fe/api/hooks/account-lists/use-follow-requests';
|
||||
import { getNotificationStatus } from 'pl-fe/features/notifications/components/notification';
|
||||
import { normalizeNotification } from 'pl-fe/normalizers/notification';
|
||||
import { getFilters, regexFromFilters } from 'pl-fe/selectors';
|
||||
|
@ -74,9 +75,14 @@ const updateNotifications = (notification: BaseNotification) =>
|
|||
statuses: [getNotificationStatus(notification) as any],
|
||||
}));
|
||||
|
||||
|
||||
if (showInColumn) {
|
||||
const normalizedNotification = normalizeNotification(notification);
|
||||
|
||||
if (normalizedNotification.type === 'follow_request') {
|
||||
normalizedNotification.sample_account_ids.forEach(appendFollowRequest);
|
||||
}
|
||||
|
||||
dispatch<NotificationsUpdateAction>({
|
||||
type: NOTIFICATIONS_UPDATE,
|
||||
notification: normalizedNotification,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
import { InfiniteData, useInfiniteQuery, useMutation } from '@tanstack/react-query';
|
||||
import { type InfiniteData, useInfiniteQuery, useMutation } from '@tanstack/react-query';
|
||||
|
||||
import { importEntities } from 'pl-fe/actions/importer';
|
||||
import { minifyList } from 'pl-fe/api/normalizers/minify-list';
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
import { useInfiniteQuery, useMutation, type InfiniteData } from '@tanstack/react-query';
|
||||
|
||||
import { minifyAccountList } from 'pl-fe/api/normalizers/minify-list';
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
|
||||
import type { PaginatedResponse, PlApiClient } from 'pl-api';
|
||||
|
||||
const appendFollowRequest = (accountId: string) =>
|
||||
queryClient.setQueryData<InfiniteData<ReturnType<typeof minifyAccountList>>>(['accountsLists', 'followRequests'], (data) => {
|
||||
if (!data || data.pages.some(page => page.items.includes(accountId))) return data;
|
||||
|
||||
return {
|
||||
...data,
|
||||
pages: data.pages.map((page, index) => index === 0 ? ({ ...page, items: [accountId, ...page.items] }) : page),
|
||||
};
|
||||
});
|
||||
|
||||
const removeFollowRequest = (accountId: string) =>
|
||||
queryClient.setQueryData<InfiniteData<ReturnType<typeof minifyAccountList>>>(['accountsLists', 'followRequests'], (data) => data ? {
|
||||
...data,
|
||||
pages: data.pages.map(({ items, ...page }) => ({ ...page, items: items.filter((id) => id !== accountId) })),
|
||||
} : undefined);
|
||||
|
||||
const useFollowRequests = () => {
|
||||
const client = useClient();
|
||||
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['accountsLists', 'followRequests'],
|
||||
queryFn: ({ pageParam }) => pageParam.next?.() || client.myAccount.getFollowRequests().then(minifyAccountList),
|
||||
initialPageParam: { previous: null, next: null, items: [], partial: false } as PaginatedResponse<string>,
|
||||
getNextPageParam: (page) => page.next ? page : undefined,
|
||||
select: (data) => data.pages.map(page => page.items).flat(),
|
||||
});
|
||||
};
|
||||
|
||||
const useAcceptFollowRequestMutation = (accountId: string) => {
|
||||
const client = useClient();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['accountsLists', 'followRequests', accountId],
|
||||
mutationFn: () => client.myAccount.acceptFollowRequest(accountId),
|
||||
onSettled: () => removeFollowRequest(accountId),
|
||||
});
|
||||
};
|
||||
|
||||
const useRejectFollowRequestMutation = (accountId: string) => {
|
||||
const client = useClient();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['accountsLists', 'followRequests', accountId],
|
||||
mutationFn: () => client.myAccount.rejectFollowRequest(accountId),
|
||||
onSettled: () => removeFollowRequest(accountId),
|
||||
});
|
||||
};
|
||||
|
||||
const prefetchFollowRequests = (client: PlApiClient) => queryClient.prefetchInfiniteQuery({
|
||||
queryKey: ['accountsLists', 'followRequests'],
|
||||
queryFn: ({ pageParam }) => pageParam.next?.() || client.myAccount.getFollowRequests().then(minifyAccountList),
|
||||
initialPageParam: { previous: null, next: null, items: [], partial: false } as PaginatedResponse<string>,
|
||||
});
|
||||
|
||||
export { appendFollowRequest, useFollowRequests, useAcceptFollowRequestMutation, useRejectFollowRequestMutation, prefetchFollowRequests };
|
|
@ -1,21 +1,22 @@
|
|||
import React from 'react';
|
||||
|
||||
import { authorizeFollowRequest, rejectFollowRequest } from 'pl-fe/actions/accounts';
|
||||
import { useAcceptFollowRequestMutation, useRejectFollowRequestMutation } from 'pl-fe/api/hooks/account-lists/use-follow-requests';
|
||||
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
|
||||
import Account from 'pl-fe/components/account';
|
||||
import { AuthorizeRejectButtons } from 'pl-fe/components/authorize-reject-buttons';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
|
||||
interface IAccountAuthorize {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const AccountAuthorize: React.FC<IAccountAuthorize> = ({ id }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { account } = useAccount(id);
|
||||
|
||||
const onAuthorize = () => dispatch(authorizeFollowRequest(id));
|
||||
const onReject = () => dispatch(rejectFollowRequest(id));
|
||||
const { mutate: authorizeFollowRequest } = useAcceptFollowRequestMutation(id);
|
||||
const { mutate: rejectFollowRequest } = useRejectFollowRequestMutation(id);
|
||||
|
||||
const onAuthorize = () => authorizeFollowRequest();
|
||||
const onReject = () => rejectFollowRequest();
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import debounce from 'lodash/debounce';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { fetchFollowRequests, expandFollowRequests } from 'pl-fe/actions/accounts';
|
||||
import { useFollowRequests } from 'pl-fe/api/hooks/account-lists/use-follow-requests';
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
|
||||
import AccountAuthorize from './components/account-authorize';
|
||||
|
||||
|
@ -15,20 +12,10 @@ const messages = defineMessages({
|
|||
heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' },
|
||||
});
|
||||
|
||||
const handleLoadMore = debounce((dispatch) => {
|
||||
dispatch(expandFollowRequests());
|
||||
}, 300, { leading: true });
|
||||
|
||||
const FollowRequests: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const accountIds = useAppSelector((state) => state.user_lists.follow_requests.items);
|
||||
const hasMore = useAppSelector((state) => !!state.user_lists.follow_requests.next);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchFollowRequests());
|
||||
}, []);
|
||||
const { data: accountIds, isLoading, hasNextPage, fetchNextPage } = useFollowRequests();
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
|
@ -43,8 +30,9 @@ const FollowRequests: React.FC = () => {
|
|||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<ScrollableList
|
||||
onLoadMore={() => handleLoadMore(dispatch)}
|
||||
hasMore={hasMore}
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
emptyMessage={emptyMessage}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
|
|
|
@ -6,10 +6,9 @@ import {
|
|||
unblockAccount,
|
||||
muteAccount,
|
||||
unmuteAccount,
|
||||
authorizeFollowRequest,
|
||||
rejectFollowRequest,
|
||||
biteAccount,
|
||||
} from 'pl-fe/actions/accounts';
|
||||
import { useAcceptFollowRequestMutation, useRejectFollowRequestMutation } from 'pl-fe/api/hooks/account-lists/use-follow-requests';
|
||||
import { useFollow } from 'pl-fe/api/hooks/accounts/use-follow';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
|
@ -63,6 +62,9 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
const { isLoggedIn, me } = useLoggedIn();
|
||||
const { follow, unfollow } = useFollow();
|
||||
|
||||
const { mutate: authorizeFollowRequest } = useAcceptFollowRequestMutation(account.id);
|
||||
const { mutate: rejectFollowRequest } = useRejectFollowRequestMutation(account.id);
|
||||
|
||||
const handleFollow = () => {
|
||||
if (account.relationship?.following || account.relationship?.requested) {
|
||||
unfollow(account.id);
|
||||
|
@ -88,11 +90,11 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
};
|
||||
|
||||
const handleAuthorize = () => {
|
||||
dispatch(authorizeFollowRequest(account.id));
|
||||
authorizeFollowRequest();
|
||||
};
|
||||
|
||||
const handleReject = () => {
|
||||
dispatch(rejectFollowRequest(account.id));
|
||||
rejectFollowRequest();
|
||||
};
|
||||
|
||||
const handleBite = () => {
|
||||
|
|
|
@ -2,7 +2,6 @@ import clsx from 'clsx';
|
|||
import React, { Suspense, lazy, useEffect, useRef } from 'react';
|
||||
import { Redirect, Switch, useHistory, useLocation } from 'react-router-dom';
|
||||
|
||||
import { fetchFollowRequests } from 'pl-fe/actions/accounts';
|
||||
import { fetchConfig, fetchReports, fetchUsers } from 'pl-fe/actions/admin';
|
||||
import { fetchCustomEmojis } from 'pl-fe/actions/custom-emojis';
|
||||
import { fetchDraftStatuses } from 'pl-fe/actions/draft-statuses';
|
||||
|
@ -12,12 +11,14 @@ import { expandNotifications } from 'pl-fe/actions/notifications';
|
|||
import { register as registerPushNotifications } from 'pl-fe/actions/push-notifications/registerer';
|
||||
import { fetchScheduledStatuses } from 'pl-fe/actions/scheduled-statuses';
|
||||
import { fetchHomeTimeline } from 'pl-fe/actions/timelines';
|
||||
import { prefetchFollowRequests } from 'pl-fe/api/hooks/account-lists/use-follow-requests';
|
||||
import { useUserStream } from 'pl-fe/api/hooks/streaming/use-user-stream';
|
||||
import SidebarNavigation from 'pl-fe/components/sidebar-navigation';
|
||||
import ThumbNavigation from 'pl-fe/components/thumb-navigation';
|
||||
import Layout from 'pl-fe/components/ui/layout';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useDraggedFiles } from 'pl-fe/hooks/use-dragged-files';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useInstance } from 'pl-fe/hooks/use-instance';
|
||||
|
@ -362,6 +363,7 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|||
const { account } = useOwnAccount();
|
||||
const features = useFeatures();
|
||||
const vapidKey = useAppSelector(state => getVapidKey(state));
|
||||
const client = useClient();
|
||||
|
||||
const { isDropdownMenuOpen } = useUiStore();
|
||||
const standalone = useAppSelector(isStandalone);
|
||||
|
@ -409,7 +411,7 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|||
setTimeout(() => dispatch(fetchFilters()), 500);
|
||||
|
||||
if (account.locked) {
|
||||
setTimeout(() => dispatch(fetchFollowRequests()), 700);
|
||||
setTimeout(() => prefetchFollowRequests(client), 700);
|
||||
}
|
||||
|
||||
setTimeout(() => dispatch(fetchScheduledStatuses()), 900);
|
||||
|
|
|
@ -3,8 +3,6 @@ import { create } from 'mutative';
|
|||
import {
|
||||
ACCOUNT_BLOCK_SUCCESS,
|
||||
ACCOUNT_MUTE_SUCCESS,
|
||||
FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
||||
FOLLOW_REQUEST_REJECT_SUCCESS,
|
||||
type AccountsAction,
|
||||
} from '../actions/accounts';
|
||||
import {
|
||||
|
@ -87,11 +85,11 @@ const filterNotifications = (state: State, relationship: Relationship) =>
|
|||
draft.items = draft.items.filter(item => !item.sample_account_ids.includes(relationship.id));
|
||||
});
|
||||
|
||||
const filterNotificationIds = (state: State, accountIds: Array<string>, type?: string) =>
|
||||
create(state, (draft) => {
|
||||
const helper = (list: Array<NotificationGroup>) => list.filter(item => !(accountIds.includes(item.sample_account_ids[0]) && (type === undefined || type === item.type)));
|
||||
draft.items = helper(draft.items);
|
||||
});
|
||||
// const filterNotificationIds = (state: State, accountIds: Array<string>, type?: string) =>
|
||||
// create(state, (draft) => {
|
||||
// const helper = (list: Array<NotificationGroup>) => list.filter(item => !(accountIds.includes(item.sample_account_ids[0]) && (type === undefined || type === item.type)));
|
||||
// draft.items = helper(draft.items);
|
||||
// });
|
||||
|
||||
const updateTop = (state: State, top: boolean) =>
|
||||
create(state, (draft) => {
|
||||
|
@ -147,9 +145,9 @@ const notifications = (state: State = initialState, action: AccountsAction | Mar
|
|||
return filterNotifications(state, action.relationship);
|
||||
case ACCOUNT_MUTE_SUCCESS:
|
||||
return action.relationship.muting_notifications ? filterNotifications(state, action.relationship) : state;
|
||||
case FOLLOW_REQUEST_AUTHORIZE_SUCCESS:
|
||||
case FOLLOW_REQUEST_REJECT_SUCCESS:
|
||||
return filterNotificationIds(state, [action.accountId], 'follow_request');
|
||||
// case FOLLOW_REQUEST_AUTHORIZE_SUCCESS:
|
||||
// case FOLLOW_REQUEST_REJECT_SUCCESS:
|
||||
// return filterNotificationIds(state, [action.accountId], 'follow_request');
|
||||
case MARKER_FETCH_SUCCESS:
|
||||
case MARKER_SAVE_SUCCESS:
|
||||
return importMarker(state, action.marker);
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import { create } from 'mutative';
|
||||
|
||||
import {
|
||||
FOLLOW_REQUESTS_FETCH_SUCCESS,
|
||||
FOLLOW_REQUESTS_EXPAND_SUCCESS,
|
||||
FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
||||
FOLLOW_REQUEST_REJECT_SUCCESS,
|
||||
PINNED_ACCOUNTS_FETCH_SUCCESS,
|
||||
type AccountsAction,
|
||||
} from 'pl-fe/actions/accounts';
|
||||
import { PINNED_ACCOUNTS_FETCH_SUCCESS, type AccountsAction } from 'pl-fe/actions/accounts';
|
||||
import { FAMILIAR_FOLLOWERS_FETCH_SUCCESS, type FamiliarFollowersAction } from 'pl-fe/actions/familiar-followers';
|
||||
import {
|
||||
GROUP_BLOCKS_FETCH_REQUEST,
|
||||
|
@ -16,9 +9,8 @@ import {
|
|||
GROUP_UNBLOCK_SUCCESS,
|
||||
type GroupsAction,
|
||||
} from 'pl-fe/actions/groups';
|
||||
import { NOTIFICATIONS_UPDATE, type NotificationsAction } from 'pl-fe/actions/notifications';
|
||||
|
||||
import type { Account, NotificationGroup, PaginatedResponse } from 'pl-api';
|
||||
import type { Account, PaginatedResponse } from 'pl-api';
|
||||
|
||||
interface List {
|
||||
next: (() => Promise<PaginatedResponse<Account>>) | null;
|
||||
|
@ -61,50 +53,8 @@ const normalizeList = (state: State, path: NestedListPath | ListPath, accounts:
|
|||
}
|
||||
});
|
||||
|
||||
const appendToList = (state: State, path: NestedListPath | ListPath, accounts: Array<Pick<Account, 'id'>>, next: (() => any) | null = null) =>
|
||||
create(state, (draft) => {
|
||||
let list: List;
|
||||
|
||||
if (path.length === 1) {
|
||||
list = draft[path[0]];
|
||||
} else {
|
||||
list = draft[path[0]][path[1]];
|
||||
}
|
||||
|
||||
list.next = next;
|
||||
list.isLoading = false;
|
||||
list.items = [...new Set([...list.items, ...accounts.map(item => item.id)])];
|
||||
});
|
||||
|
||||
const removeFromList = (state: State, path: NestedListPath | ListPath, accountId: string) =>
|
||||
create(state, (draft) => {
|
||||
let list: List;
|
||||
|
||||
if (path.length === 1) {
|
||||
list = draft[path[0]];
|
||||
} else {
|
||||
list = draft[path[0]][path[1]];
|
||||
}
|
||||
|
||||
list.items = list.items.filter(item => item !== accountId);
|
||||
});
|
||||
|
||||
const normalizeFollowRequest = (state: State, notification: NotificationGroup) =>
|
||||
create(state, (draft) => {
|
||||
draft.follow_requests.items = [...new Set([...notification.sample_account_ids, ...draft.follow_requests.items])];
|
||||
});
|
||||
|
||||
const userLists = (state = initialState, action: AccountsAction | FamiliarFollowersAction | GroupsAction | NotificationsAction): State => {
|
||||
const userLists = (state = initialState, action: AccountsAction | FamiliarFollowersAction | GroupsAction): State => {
|
||||
switch (action.type) {
|
||||
case NOTIFICATIONS_UPDATE:
|
||||
return action.notification.type === 'follow_request' ? normalizeFollowRequest(state, action.notification) : state;
|
||||
case FOLLOW_REQUESTS_FETCH_SUCCESS:
|
||||
return normalizeList(state, ['follow_requests'], action.accounts, action.next);
|
||||
case FOLLOW_REQUESTS_EXPAND_SUCCESS:
|
||||
return appendToList(state, ['follow_requests'], action.accounts, action.next);
|
||||
case FOLLOW_REQUEST_AUTHORIZE_SUCCESS:
|
||||
case FOLLOW_REQUEST_REJECT_SUCCESS:
|
||||
return removeFromList(state, ['follow_requests'], action.accountId);
|
||||
case PINNED_ACCOUNTS_FETCH_SUCCESS:
|
||||
return normalizeList(state, ['pinned', action.accountId], action.accounts, action.next);
|
||||
case FAMILIAR_FOLLOWERS_FETCH_SUCCESS:
|
||||
|
|
Loading…
Reference in a new issue