pl-fe: remove user lists reducer

Signed-off-by: mkljczk <git@mkljczk.pl>
This commit is contained in:
mkljczk 2024-12-05 10:39:11 +01:00
parent 105c87c64d
commit 05bb838577
9 changed files with 68 additions and 205 deletions

View file

@ -1,99 +1,12 @@
import { getClient } from '../api';
import { importEntities } from './importer';
import type { Account, PaginatedResponse } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';
const GROUP_BLOCKS_FETCH_REQUEST = 'GROUP_BLOCKS_FETCH_REQUEST' as const;
const GROUP_BLOCKS_FETCH_SUCCESS = 'GROUP_BLOCKS_FETCH_SUCCESS' as const;
const GROUP_BLOCKS_FETCH_FAIL = 'GROUP_BLOCKS_FETCH_FAIL' as const;
const GROUP_UNBLOCK_REQUEST = 'GROUP_UNBLOCK_REQUEST' as const;
const GROUP_UNBLOCK_SUCCESS = 'GROUP_UNBLOCK_SUCCESS' as const;
const GROUP_UNBLOCK_FAIL = 'GROUP_UNBLOCK_FAIL' as const;
const groupKick = (groupId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
return getClient(getState).experimental.groups.kickGroupUsers(groupId, [accountId]);
};
const fetchGroupBlocks = (groupId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(fetchGroupBlocksRequest(groupId));
return getClient(getState).experimental.groups.getGroupBlocks(groupId).then(response => {
dispatch(importEntities({ accounts: response.items }));
dispatch(fetchGroupBlocksSuccess(groupId, response.items, response.next));
}).catch(error => {
dispatch(fetchGroupBlocksFail(groupId, error));
});
};
const fetchGroupBlocksRequest = (groupId: string) => ({
type: GROUP_BLOCKS_FETCH_REQUEST,
groupId,
});
const fetchGroupBlocksSuccess = (groupId: string, accounts: Array<Account>, next: (() => Promise<PaginatedResponse<Account>>) | null) => ({
type: GROUP_BLOCKS_FETCH_SUCCESS,
groupId,
accounts,
next,
});
const fetchGroupBlocksFail = (groupId: string, error: unknown) => ({
type: GROUP_BLOCKS_FETCH_FAIL,
groupId,
error,
skipNotFound: true,
});
const groupUnblock = (groupId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch(groupUnblockRequest(groupId, accountId));
return getClient(getState).experimental.groups.unblockGroupUsers(groupId, [accountId])
.then(() => dispatch(groupUnblockSuccess(groupId, accountId)))
.catch(err => dispatch(groupUnblockFail(groupId, accountId, err)));
};
const groupUnblockRequest = (groupId: string, accountId: string) => ({
type: GROUP_UNBLOCK_REQUEST,
groupId,
accountId,
});
const groupUnblockSuccess = (groupId: string, accountId: string) => ({
type: GROUP_UNBLOCK_SUCCESS,
groupId,
accountId,
});
const groupUnblockFail = (groupId: string, accountId: string, error: unknown) => ({
type: GROUP_UNBLOCK_FAIL,
groupId,
accountId,
error,
});
type GroupsAction =
| ReturnType<typeof fetchGroupBlocksRequest>
| ReturnType<typeof fetchGroupBlocksSuccess>
| ReturnType<typeof fetchGroupBlocksFail>
| ReturnType<typeof groupUnblockRequest>
| ReturnType<typeof groupUnblockSuccess>
| ReturnType<typeof groupUnblockFail>
export {
GROUP_BLOCKS_FETCH_REQUEST,
GROUP_BLOCKS_FETCH_SUCCESS,
GROUP_BLOCKS_FETCH_FAIL,
GROUP_UNBLOCK_REQUEST,
GROUP_UNBLOCK_SUCCESS,
GROUP_UNBLOCK_FAIL,
groupKick,
fetchGroupBlocks,
groupUnblock,
type GroupsAction,
};

View file

@ -1,19 +0,0 @@
import { Entities } from 'pl-fe/entity-store/entities';
import { useCreateEntity } from 'pl-fe/entity-store/hooks/use-create-entity';
import { useClient } from 'pl-fe/hooks/use-client';
import type { Group } from 'pl-api';
import type { Account } from 'pl-fe/normalizers/account';
const useBlockGroupMember = (group: Pick<Group, 'id'>, account: Pick<Account, 'id'>) => {
const client = useClient();
const { createEntity } = useCreateEntity(
[Entities.GROUP_MEMBERSHIPS, account.id],
(accountIds: string[]) => client.experimental.groups.blockGroupUsers(group.id, accountIds),
);
return createEntity;
};
export { useBlockGroupMember };

View file

@ -13,7 +13,6 @@ import { initReport, ReportableEntities } from 'pl-fe/actions/reports';
import { changeSetting } from 'pl-fe/actions/settings';
import { deleteStatus, editStatus, toggleMuteStatus } from 'pl-fe/actions/statuses';
import { deleteFromTimelines } from 'pl-fe/actions/timelines';
import { useBlockGroupMember } from 'pl-fe/api/hooks/groups/use-block-group-member';
import { useDeleteGroupStatus } from 'pl-fe/api/hooks/groups/use-delete-group-status';
import { useGroup } from 'pl-fe/api/hooks/groups/use-group';
import { useGroupRelationship } from 'pl-fe/api/hooks/groups/use-group-relationship';
@ -30,6 +29,7 @@ import { useInstance } from 'pl-fe/hooks/use-instance';
import { useOwnAccount } from 'pl-fe/hooks/use-own-account';
import { useSettings } from 'pl-fe/hooks/use-settings';
import { useChats } from 'pl-fe/queries/chats';
import { useBlockGroupUserMutation } from 'pl-fe/queries/groups/use-group-blocks';
import { useTranslationLanguages } from 'pl-fe/queries/instance/use-translation-languages';
import { RootState } from 'pl-fe/store';
import { useModalsStore } from 'pl-fe/stores/modals';
@ -590,7 +590,7 @@ const MenuButton: React.FC<IMenuButton> = ({
const { openModal } = useModalsStore();
const { group } = useGroup((status.group as Group)?.id as string);
const deleteGroupStatus = useDeleteGroupStatus(group as Group, status.id);
const blockGroupMember = useBlockGroupMember(group as Group, status.account);
const { mutate: blockGroupMember } = useBlockGroupUserMutation(status.group?.id as string, status.account.id);
const { getOrCreateChatByAccountId } = useChats();
const { groupRelationship } = useGroupRelationship(status.group_id || undefined);
@ -762,8 +762,8 @@ const MenuButton: React.FC<IMenuButton> = ({
message: intl.formatMessage(messages.groupBlockFromGroupMessage, { name: status.account.username }),
confirm: intl.formatMessage(messages.groupBlockConfirm),
onConfirm: () => {
blockGroupMember([status.account_id], {
onSuccess() {
blockGroupMember(undefined, {
onSuccess: () => {
toast.success(intl.formatMessage(messages.blocked, { name: account?.acct }));
},
});

View file

@ -5,7 +5,6 @@ import { defineMessages, useIntl } from 'react-intl';
import { groupKick } from 'pl-fe/actions/groups';
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
import { useBlockGroupMember } from 'pl-fe/api/hooks/groups/use-block-group-member';
import { useDemoteGroupMember } from 'pl-fe/api/hooks/groups/use-demote-group-member';
import { usePromoteGroupMember } from 'pl-fe/api/hooks/groups/use-promote-group-member';
import Account from 'pl-fe/components/account';
@ -15,6 +14,7 @@ import { deleteEntities } from 'pl-fe/entity-store/actions';
import { Entities } from 'pl-fe/entity-store/entities';
import PlaceholderAccount from 'pl-fe/features/placeholder/components/placeholder-account';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useBlockGroupUserMutation } from 'pl-fe/queries/groups/use-group-blocks';
import { useModalsStore } from 'pl-fe/stores/modals';
import toast from 'pl-fe/toast';
@ -53,7 +53,7 @@ const GroupMemberListItem = ({ member, group }: IGroupMemberListItem) => {
const intl = useIntl();
const { openModal } = useModalsStore();
const blockGroupMember = useBlockGroupMember(group, member.account);
const { mutate: blockGroupMember } = useBlockGroupUserMutation(group.id, member.account.id);
const promoteGroupMember = usePromoteGroupMember(group, member);
const demoteGroupMember = useDemoteGroupMember(group, member);
@ -85,7 +85,7 @@ const GroupMemberListItem = ({ member, group }: IGroupMemberListItem) => {
message: intl.formatMessage(messages.blockFromGroupMessage, { name: account?.username }),
confirm: intl.formatMessage(messages.blockConfirm),
onConfirm: () => {
blockGroupMember([member.account.id], {
blockGroupMember(undefined, {
onSuccess() {
dispatch(deleteEntities([member.id], Entities.GROUP_MEMBERSHIPS));
toast.success(intl.formatMessage(messages.blocked, { name: account?.acct }));

View file

@ -1,7 +1,6 @@
import React, { useEffect } from 'react';
import React from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { fetchGroupBlocks, groupUnblock } from 'pl-fe/actions/groups';
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
import { useGroup } from 'pl-fe/api/hooks/groups/use-group';
import Account from 'pl-fe/components/account';
@ -10,8 +9,7 @@ import Button from 'pl-fe/components/ui/button';
import Column from 'pl-fe/components/ui/column';
import HStack from 'pl-fe/components/ui/hstack';
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 { useGroupBlocks, useUnblockGroupUserMutation } from 'pl-fe/queries/groups/use-group-blocks';
import toast from 'pl-fe/toast';
import ColumnForbidden from '../ui/components/column-forbidden';
@ -31,14 +29,16 @@ interface IBlockedMember {
const BlockedMember: React.FC<IBlockedMember> = ({ accountId, groupId }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const { account } = useAccount(accountId);
const { mutate: unblockGroupUser } = useUnblockGroupUserMutation(groupId, accountId);
if (!account) return null;
const handleUnblock = () =>
dispatch(groupUnblock(groupId, accountId))
.then(() => toast.success(intl.formatMessage(messages.unblocked, { name: account.acct })));
unblockGroupUser(undefined, {
onSuccess: () => toast.success(intl.formatMessage(messages.unblocked, { name: account.acct })),
});
return (
<HStack space={1} alignItems='center' justifyContent='between' className='p-2.5'>
@ -61,16 +61,11 @@ interface IGroupBlockedMembers {
const GroupBlockedMembers: React.FC<IGroupBlockedMembers> = ({ params }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const groupId = params?.groupId;
const { group } = useGroup(groupId);
const accountIds = useAppSelector((state) => state.user_lists.group_blocks[groupId]?.items);
useEffect(() => {
dispatch(fetchGroupBlocks(groupId));
}, [groupId]);
const { data: accountIds } = useGroupBlocks(groupId);
if (!group || !group.relationship || !accountIds) {
return (

View file

@ -0,0 +1,53 @@
import { useMutation, type InfiniteData } from '@tanstack/react-query';
import { makePaginatedResponseQuery } from 'pl-fe/api/utils/make-paginated-response-query';
import { minifyAccountList } from 'pl-fe/api/utils/minify-list';
import { useClient } from 'pl-fe/hooks/use-client';
import { queryClient } from 'pl-fe/queries/client';
const appendGroupBlock = (groupId: string, accountId: string) =>
queryClient.setQueryData<InfiniteData<ReturnType<typeof minifyAccountList>>>(['accountsLists', 'groupBlocks', groupId], (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 removeGroupBlock = (groupId: string, accountId: string) =>
queryClient.setQueryData<InfiniteData<ReturnType<typeof minifyAccountList>>>(['accountsLists', 'groupBlocks', groupId], (data) => data ? {
...data,
pages: data.pages.map(({ items, ...page }) => ({ ...page, items: items.filter((id) => id !== accountId) })),
} : undefined);
const useGroupBlocks = makePaginatedResponseQuery(
(groupId: string) => ['accountsLists', 'groupBlocks', groupId],
(client, [groupId]) => client.experimental.groups.getGroupBlocks(groupId).then(minifyAccountList),
);
const useBlockGroupUserMutation = (groupId: string, accountId: string) => {
const client = useClient();
return useMutation({
mutationKey: ['accountsLists', 'groupBlocks', groupId, accountId],
mutationFn: () => client.experimental.groups.blockGroupUsers(groupId, [accountId]),
onSettled: () => appendGroupBlock(groupId, accountId),
});
};
const useUnblockGroupUserMutation = (groupId: string, accountId: string) => {
const client = useClient();
return useMutation({
mutationKey: ['accountsLists', 'groupBlocks', groupId, accountId],
mutationFn: () => client.experimental.groups.unblockGroupUsers(groupId, [accountId]),
onSettled: () => removeGroupBlock(groupId, accountId),
});
};
export {
useGroupBlocks,
useBlockGroupUserMutation,
useUnblockGroupUserMutation,
};

View file

@ -36,7 +36,6 @@ import status_lists from './status-lists';
import statuses from './statuses';
import tags from './tags';
import timelines from './timelines';
import user_lists from './user-lists';
const reducers = {
accounts_meta,
@ -72,7 +71,6 @@ const reducers = {
statuses,
tags,
timelines,
user_lists,
};
const appReducer = combineReducers(reducers);

View file

@ -1,9 +0,0 @@
import reducer from './user-lists';
describe('user_lists reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {} as any)).toMatchObject({
group_blocks: {},
});
});
});

View file

@ -1,68 +0,0 @@
import { create } from 'mutative';
import {
GROUP_BLOCKS_FETCH_REQUEST,
GROUP_BLOCKS_FETCH_SUCCESS,
GROUP_BLOCKS_FETCH_FAIL,
GROUP_UNBLOCK_SUCCESS,
type GroupsAction,
} from 'pl-fe/actions/groups';
import type { Account, PaginatedResponse } from 'pl-api';
interface List {
next: (() => Promise<PaginatedResponse<Account>>) | null;
items: Array<string>;
isLoading: boolean;
}
type NestedListKey = 'group_blocks';
type State = Record<NestedListKey, Record<string, List>>;
const initialState: State = {
group_blocks: {},
};
type NestedListPath = [NestedListKey, string];
const normalizeList = (state: State, path: NestedListPath, accounts: Array<Pick<Account, 'id'>>, next: (() => Promise<PaginatedResponse<any>>) | null = null) =>
create(state, (draft) => {
const list = draft[path[0]][path[1]];
const newList = { ...list, next, items: accounts.map(item => item.id), isLoading: false };
draft[path[0]][path[1]] = newList;
});
const userLists = (state = initialState, action: GroupsAction): State => {
switch (action.type) {
case GROUP_BLOCKS_FETCH_SUCCESS:
return normalizeList(state, ['group_blocks', action.groupId], action.accounts, action.next);
case GROUP_BLOCKS_FETCH_REQUEST:
return create(state, (draft) => {
draft.group_blocks[action.groupId] = {
items: [],
next: null,
isLoading: true,
};
});
case GROUP_BLOCKS_FETCH_FAIL:
return create(state, (draft) => {
draft.group_blocks[action.groupId] = {
items: [],
next: null,
isLoading: false,
};
});
case GROUP_UNBLOCK_SUCCESS:
return create(state, (draft) => {
const list = draft.group_blocks[action.groupId];
if (list.items) list.items = list.items.filter(item => item !== action.accountId);
});
default:
return state;
}
};
export {
userLists as default,
};