From fbbcbdce3f3c2c2d7c69bf3371ba0962024b9ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Sun, 22 Feb 2026 20:08:16 +0100 Subject: [PATCH] nicolium: groups migrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/pl-api/lib/entities/group-member.ts | 1 + .../pl-api/lib/entities/group-relationship.ts | 2 +- packages/pl-fe/src/api/batcher.ts | 9 ++ .../hooks/groups/use-demote-group-member.ts | 27 ------ .../groups/use-group-membership-requests.ts | 54 ----------- .../hooks/groups/use-group-relationship.ts | 30 ------ .../hooks/groups/use-group-relationships.ts | 25 ----- .../pl-fe/src/api/hooks/groups/use-group.ts | 45 --------- .../pl-fe/src/api/hooks/groups/use-groups.ts | 35 ------- .../src/api/hooks/groups/use-join-group.ts | 25 ----- .../src/api/hooks/groups/use-leave-group.ts | 25 ----- .../hooks/groups/use-promote-group-member.ts | 27 ------ packages/pl-fe/src/components/group-card.tsx | 77 ++++++++------- .../src/components/status-action-bar.tsx | 13 ++- .../group/components/group-action-button.tsx | 75 ++++++--------- .../components/group-member-list-item.tsx | 13 +-- .../group/components/group-options-button.tsx | 9 +- .../components/discover/group-list-item.tsx | 21 ++-- .../features/ui/components/compose-button.tsx | 6 +- .../ui/components/panels/my-groups-panel.tsx | 12 ++- packages/pl-fe/src/layouts/group-layout.tsx | 12 +-- .../pl-fe/src/pages/groups/edit-group.tsx | 5 +- .../pages/groups/group-blocked-members.tsx | 4 +- .../pl-fe/src/pages/groups/group-gallery.tsx | 4 +- .../pl-fe/src/pages/groups/group-members.tsx | 20 ++-- .../groups/group-membership-requests.tsx | 87 +++++++++-------- packages/pl-fe/src/pages/groups/groups.tsx | 24 ++--- .../pl-fe/src/pages/groups/manage-group.tsx | 6 +- .../src/pages/timelines/group-timeline.tsx | 4 +- .../src/queries/groups/use-group-members.ts | 81 +++++++++++++++- .../queries/groups/use-group-relationship.ts | 96 +++++++++++++++++++ .../pl-fe/src/queries/groups/use-group.ts | 33 +++++++ .../pl-fe/src/queries/groups/use-groups.ts | 25 +++++ .../pl-fe/src/queries/utils/minify-list.ts | 13 +++ 34 files changed, 446 insertions(+), 499 deletions(-) delete mode 100644 packages/pl-fe/src/api/hooks/groups/use-demote-group-member.ts delete mode 100644 packages/pl-fe/src/api/hooks/groups/use-group-membership-requests.ts delete mode 100644 packages/pl-fe/src/api/hooks/groups/use-group-relationship.ts delete mode 100644 packages/pl-fe/src/api/hooks/groups/use-group-relationships.ts delete mode 100644 packages/pl-fe/src/api/hooks/groups/use-group.ts delete mode 100644 packages/pl-fe/src/api/hooks/groups/use-groups.ts delete mode 100644 packages/pl-fe/src/api/hooks/groups/use-join-group.ts delete mode 100644 packages/pl-fe/src/api/hooks/groups/use-leave-group.ts delete mode 100644 packages/pl-fe/src/api/hooks/groups/use-promote-group-member.ts create mode 100644 packages/pl-fe/src/queries/groups/use-group-relationship.ts create mode 100644 packages/pl-fe/src/queries/groups/use-group.ts create mode 100644 packages/pl-fe/src/queries/groups/use-groups.ts diff --git a/packages/pl-api/lib/entities/group-member.ts b/packages/pl-api/lib/entities/group-member.ts index 1ec0f99c2..256af9bcc 100644 --- a/packages/pl-api/lib/entities/group-member.ts +++ b/packages/pl-api/lib/entities/group-member.ts @@ -5,6 +5,7 @@ import { accountSchema } from './account'; enum GroupRoles { OWNER = 'owner', ADMIN = 'admin', + MODERATOR = 'moderator', USER = 'user', } diff --git a/packages/pl-api/lib/entities/group-relationship.ts b/packages/pl-api/lib/entities/group-relationship.ts index 2ef313133..8c8d1481e 100644 --- a/packages/pl-api/lib/entities/group-relationship.ts +++ b/packages/pl-api/lib/entities/group-relationship.ts @@ -8,7 +8,7 @@ import { GroupRoles } from './group-member'; const groupRelationshipSchema = v.object({ id: v.string(), member: v.fallback(v.boolean(), false), - role: v.fallback(v.enum(GroupRoles), GroupRoles.USER), + role: v.fallback(v.optional(v.enum(GroupRoles)), undefined), requested: v.fallback(v.boolean(), false), }); diff --git a/packages/pl-fe/src/api/batcher.ts b/packages/pl-fe/src/api/batcher.ts index 5a9482d87..01f930f23 100644 --- a/packages/pl-fe/src/api/batcher.ts +++ b/packages/pl-fe/src/api/batcher.ts @@ -11,6 +11,14 @@ const relationships = memoize((client: PlApiClient) => }), ); +const groupRelationships = memoize((client: PlApiClient) => + create({ + fetcher: (ids: string[]) => client.experimental.groups.getGroupRelationships(ids), + resolver: keyResolver('id'), + scheduler: bufferScheduler(200), + }), +); + // TODO: proper multi-client support const translations = memoize((lang: string, client: PlApiClient) => create({ @@ -22,6 +30,7 @@ const translations = memoize((lang: string, client: PlApiClient) => const batcher = { relationships, + groupRelationships, translations, }; diff --git a/packages/pl-fe/src/api/hooks/groups/use-demote-group-member.ts b/packages/pl-fe/src/api/hooks/groups/use-demote-group-member.ts deleted file mode 100644 index 9e76e5686..000000000 --- a/packages/pl-fe/src/api/hooks/groups/use-demote-group-member.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as v from 'valibot'; - -import { Entities } from '@/entity-store/entities'; -import { useCreateEntity } from '@/entity-store/hooks/use-create-entity'; -import { useClient } from '@/hooks/use-client'; - -import type { Group, GroupMember, GroupRole } from 'pl-api'; - -const useDemoteGroupMember = (group: Pick, groupMember: Pick) => { - const client = useClient(); - - const { createEntity } = useCreateEntity( - [Entities.GROUP_MEMBERSHIPS, groupMember.id], - ({ account_ids, role }: { account_ids: string[]; role: GroupRole }) => - client.experimental.groups.demoteGroupUsers(group.id, account_ids, role), - { - schema: v.pipe( - v.any(), - v.transform((arr) => arr[0]), - ), - }, - ); - - return createEntity; -}; - -export { useDemoteGroupMember }; diff --git a/packages/pl-fe/src/api/hooks/groups/use-group-membership-requests.ts b/packages/pl-fe/src/api/hooks/groups/use-group-membership-requests.ts deleted file mode 100644 index cc19ce139..000000000 --- a/packages/pl-fe/src/api/hooks/groups/use-group-membership-requests.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { GroupRoles } from 'pl-api'; - -import { Entities } from '@/entity-store/entities'; -import { useDismissEntity } from '@/entity-store/hooks/use-dismiss-entity'; -import { useEntities } from '@/entity-store/hooks/use-entities'; -import { useClient } from '@/hooks/use-client'; - -import { useGroupRelationship } from './use-group-relationship'; - -import type { ExpandedEntitiesPath } from '@/entity-store/hooks/types'; -import type { Account } from 'pl-api'; - -const useGroupMembershipRequests = (groupId: string) => { - const client = useClient(); - const path: ExpandedEntitiesPath = [Entities.ACCOUNTS, 'membership_requests', groupId]; - - const { groupRelationship: relationship } = useGroupRelationship(groupId); - - const { entities, invalidate, fetchEntities, ...rest } = useEntities( - path, - () => client.experimental.groups.getGroupMembershipRequests(groupId), - { - enabled: relationship?.role === GroupRoles.OWNER || relationship?.role === GroupRoles.ADMIN, - }, - ); - - const { dismissEntity: authorize } = useDismissEntity(path, async (accountId: string) => { - const response = await client.experimental.groups.acceptGroupMembershipRequest( - groupId, - accountId, - ); - invalidate(); - return response; - }); - - const { dismissEntity: reject } = useDismissEntity(path, async (accountId: string) => { - const response = await client.experimental.groups.rejectGroupMembershipRequest( - groupId, - accountId, - ); - invalidate(); - return response; - }); - - return { - accounts: entities, - refetch: fetchEntities, - authorize, - reject, - ...rest, - }; -}; - -export { useGroupMembershipRequests }; diff --git a/packages/pl-fe/src/api/hooks/groups/use-group-relationship.ts b/packages/pl-fe/src/api/hooks/groups/use-group-relationship.ts deleted file mode 100644 index dcff57403..000000000 --- a/packages/pl-fe/src/api/hooks/groups/use-group-relationship.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as v from 'valibot'; - -import { Entities } from '@/entity-store/entities'; -import { useEntity } from '@/entity-store/hooks/use-entity'; -import { useClient } from '@/hooks/use-client'; - -import type { GroupRelationship } from 'pl-api'; - -const useGroupRelationship = (groupId: string | undefined) => { - const client = useClient(); - - const { entity: groupRelationship, ...result } = useEntity( - [Entities.GROUP_RELATIONSHIPS, groupId!], - () => client.experimental.groups.getGroupRelationships([groupId!]), - { - enabled: !!groupId, - schema: v.pipe( - v.any(), - v.transform((arr) => arr[0]), - ), - }, - ); - - return { - groupRelationship, - ...result, - }; -}; - -export { useGroupRelationship }; diff --git a/packages/pl-fe/src/api/hooks/groups/use-group-relationships.ts b/packages/pl-fe/src/api/hooks/groups/use-group-relationships.ts deleted file mode 100644 index 2d6375598..000000000 --- a/packages/pl-fe/src/api/hooks/groups/use-group-relationships.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Entities } from '@/entity-store/entities'; -import { useBatchedEntities } from '@/entity-store/hooks/use-batched-entities'; -import { useClient } from '@/hooks/use-client'; -import { useLoggedIn } from '@/hooks/use-logged-in'; - -import type { GroupRelationship } from 'pl-api'; - -const useGroupRelationships = (listKey: string[], groupIds: string[]) => { - const client = useClient(); - const { isLoggedIn } = useLoggedIn(); - - const fetchGroupRelationships = (groupIds: string[]) => - client.experimental.groups.getGroupRelationships(groupIds); - - const { entityMap: relationships, ...result } = useBatchedEntities( - [Entities.GROUP_RELATIONSHIPS, ...listKey], - groupIds, - fetchGroupRelationships, - { enabled: isLoggedIn }, - ); - - return { relationships, ...result }; -}; - -export { useGroupRelationships }; diff --git a/packages/pl-fe/src/api/hooks/groups/use-group.ts b/packages/pl-fe/src/api/hooks/groups/use-group.ts deleted file mode 100644 index 76646284d..000000000 --- a/packages/pl-fe/src/api/hooks/groups/use-group.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { useLocation, useNavigate } from '@tanstack/react-router'; -import { useEffect } from 'react'; - -import { Entities } from '@/entity-store/entities'; -import { useEntity } from '@/entity-store/hooks/use-entity'; -import { useClient } from '@/hooks/use-client'; - -import { useGroupRelationship } from './use-group-relationship'; - -import type { Group } from 'pl-api'; - -const useGroup = (groupId: string, refetch = true) => { - const client = useClient(); - const location = useLocation(); - const navigate = useNavigate(); - - const { - entity: group, - isUnauthorized, - ...result - } = useEntity( - [Entities.GROUPS, groupId], - () => client.experimental.groups.getGroup(groupId), - { - refetch, - enabled: !!groupId, - }, - ); - const { groupRelationship: relationship } = useGroupRelationship(groupId); - - useEffect(() => { - if (isUnauthorized) { - localStorage.setItem('plfe:redirect_uri', location.href); - navigate({ to: '/login', replace: true }); - } - }, [isUnauthorized]); - - return { - ...result, - isUnauthorized, - group: group ? { ...group, relationship: relationship ?? null } : undefined, - }; -}; - -export { useGroup }; diff --git a/packages/pl-fe/src/api/hooks/groups/use-groups.ts b/packages/pl-fe/src/api/hooks/groups/use-groups.ts deleted file mode 100644 index 291392912..000000000 --- a/packages/pl-fe/src/api/hooks/groups/use-groups.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Entities } from '@/entity-store/entities'; -import { useEntities } from '@/entity-store/hooks/use-entities'; -import { useClient } from '@/hooks/use-client'; -import { useFeatures } from '@/hooks/use-features'; - -import { useGroupRelationships } from './use-group-relationships'; - -import type { Group } from 'pl-api'; - -const useGroups = () => { - const client = useClient(); - const features = useFeatures(); - - const { entities, ...result } = useEntities( - [Entities.GROUPS, 'search', ''], - () => client.experimental.groups.getGroups(), - { enabled: features.groups }, - ); - const { relationships } = useGroupRelationships( - ['search', ''], - entities.map((entity) => entity.id), - ); - - const groups = entities.map((group) => ({ - ...group, - relationship: relationships[group.id] || null, - })); - - return { - ...result, - groups, - }; -}; - -export { useGroups }; diff --git a/packages/pl-fe/src/api/hooks/groups/use-join-group.ts b/packages/pl-fe/src/api/hooks/groups/use-join-group.ts deleted file mode 100644 index 7b51cb6f3..000000000 --- a/packages/pl-fe/src/api/hooks/groups/use-join-group.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Entities } from '@/entity-store/entities'; -import { useCreateEntity } from '@/entity-store/hooks/use-create-entity'; -import { useClient } from '@/hooks/use-client'; - -import { useGroups } from './use-groups'; - -import type { Group } from 'pl-api'; - -const useJoinGroup = (group: Pick) => { - const client = useClient(); - const { invalidate } = useGroups(); - - const { createEntity, isSubmitting } = useCreateEntity( - [Entities.GROUP_RELATIONSHIPS, group.id], - () => client.experimental.groups.joinGroup(group.id), - ); - - return { - mutate: createEntity, - isSubmitting, - invalidate, - }; -}; - -export { useJoinGroup }; diff --git a/packages/pl-fe/src/api/hooks/groups/use-leave-group.ts b/packages/pl-fe/src/api/hooks/groups/use-leave-group.ts deleted file mode 100644 index e60ab0bff..000000000 --- a/packages/pl-fe/src/api/hooks/groups/use-leave-group.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Entities } from '@/entity-store/entities'; -import { useCreateEntity } from '@/entity-store/hooks/use-create-entity'; -import { useClient } from '@/hooks/use-client'; - -import { useGroups } from './use-groups'; - -import type { Group } from 'pl-api'; - -const useLeaveGroup = (group: Pick) => { - const client = useClient(); - const { invalidate } = useGroups(); - - const { createEntity, isSubmitting } = useCreateEntity( - [Entities.GROUP_RELATIONSHIPS, group.id], - () => client.experimental.groups.leaveGroup(group.id), - ); - - return { - mutate: createEntity, - isSubmitting, - invalidate, - }; -}; - -export { useLeaveGroup }; diff --git a/packages/pl-fe/src/api/hooks/groups/use-promote-group-member.ts b/packages/pl-fe/src/api/hooks/groups/use-promote-group-member.ts deleted file mode 100644 index 2ad3c85ba..000000000 --- a/packages/pl-fe/src/api/hooks/groups/use-promote-group-member.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as v from 'valibot'; - -import { Entities } from '@/entity-store/entities'; -import { useCreateEntity } from '@/entity-store/hooks/use-create-entity'; -import { useClient } from '@/hooks/use-client'; - -import type { Group, GroupMember, GroupRole } from 'pl-api'; - -const usePromoteGroupMember = (group: Pick, groupMember: Pick) => { - const client = useClient(); - - const { createEntity } = useCreateEntity( - [Entities.GROUP_MEMBERSHIPS, groupMember.id], - ({ account_ids, role }: { account_ids: string[]; role: GroupRole }) => - client.experimental.groups.promoteGroupUsers(group.id, account_ids, role), - { - schema: v.pipe( - v.any(), - v.transform((arr) => arr[0]), - ), - }, - ); - - return createEntity; -}; - -export { usePromoteGroupMember }; diff --git a/packages/pl-fe/src/components/group-card.tsx b/packages/pl-fe/src/components/group-card.tsx index c21eea2c1..0d0c6b7f0 100644 --- a/packages/pl-fe/src/components/group-card.tsx +++ b/packages/pl-fe/src/components/group-card.tsx @@ -8,48 +8,53 @@ import GroupHeaderImage from '@/features/group/components/group-header-image'; import GroupMemberCount from '@/features/group/components/group-member-count'; import GroupPrivacy from '@/features/group/components/group-privacy'; import GroupRelationship from '@/features/group/components/group-relationship'; +import { useGroupQuery } from '@/queries/groups/use-group'; import GroupAvatar from './groups/group-avatar'; -import type { Group } from 'pl-api'; - interface IGroupCard { - group: Group; + groupId: string; } -const GroupCard: React.FC = ({ group }) => ( - - {/* Group Cover Image */} - - +const GroupCard: React.FC = ({ groupId }) => { + const { data: group } = useGroupQuery(groupId, true); + + if (!group) return null; + + return ( + + {/* Group Cover Image */} + + + + + {/* Group Avatar */} +
+ +
+ + {/* Group Info */} + + + + + + + + + + + + +
- - {/* Group Avatar */} -
- -
- - {/* Group Info */} - - - - - - - - - - - - - -
-); + ); +}; export { GroupCard as default }; diff --git a/packages/pl-fe/src/components/status-action-bar.tsx b/packages/pl-fe/src/components/status-action-bar.tsx index 18dd1fcdc..932bdc561 100644 --- a/packages/pl-fe/src/components/status-action-bar.tsx +++ b/packages/pl-fe/src/components/status-action-bar.tsx @@ -10,8 +10,6 @@ import { deleteStatusModal, toggleStatusSensitivityModal } from '@/actions/moder import { initReport, ReportableEntities } from '@/actions/reports'; import { changeSetting } from '@/actions/settings'; import { deleteStatus, editStatus, toggleMuteStatus } from '@/actions/statuses'; -import { useGroup } from '@/api/hooks/groups/use-group'; -import { useGroupRelationship } from '@/api/hooks/groups/use-group-relationship'; import DropdownMenu from '@/components/dropdown-menu'; import StatusActionButton from '@/components/status-action-button'; import EmojiPickerDropdown from '@/features/emoji/containers/emoji-picker-dropdown-container'; @@ -26,7 +24,9 @@ import { useInstance } from '@/hooks/use-instance'; import { useOwnAccount } from '@/hooks/use-own-account'; import { useUnblockAccountMutation } from '@/queries/accounts/use-relationship'; import { useChats } from '@/queries/chats'; +import { useGroupQuery } from '@/queries/groups/use-group'; import { useBlockGroupUserMutation } from '@/queries/groups/use-group-blocks'; +import { useGroupRelationshipQuery } from '@/queries/groups/use-group-relationship'; import { useCustomEmojis } from '@/queries/instance/use-custom-emojis'; import { useTranslationLanguages } from '@/queries/instance/use-translation-languages'; import { @@ -345,7 +345,7 @@ const ReplyButton: React.FC = ({ const intl = useIntl(); const canReply = useCanInteract(status, 'can_reply'); - const { groupRelationship } = useGroupRelationship(status.group_id ?? undefined); + const { data: groupRelationship } = useGroupRelationshipQuery(status.group_id ?? undefined); let replyTitle; let replyDisabled = false; @@ -739,7 +739,7 @@ const MenuButton: React.FC = ({ const { fetchTranslation, hideTranslation } = useStatusMetaActions(); const { targetLanguage } = useStatusMeta(status.id); const { openModal } = useModalsActions(); - const { group } = useGroup((status.group as Group)?.id); + const { data: group } = useGroupQuery(status.group_id || undefined, true); const { mutate: blockGroupMember } = useBlockGroupUserMutation( status.group?.id as string, status.account.id, @@ -751,7 +751,6 @@ const MenuButton: React.FC = ({ const { mutate: unpinStatus } = useUnpinStatus(status?.id); const { mutate: unblockAccount } = useUnblockAccountMutation(status.account_id); - const { groupRelationship } = useGroupRelationship(status.group_id ?? undefined); const features = useFeatures(); const instance = useInstance(); const { autoTranslate, deleteModal, knownLanguages } = useSettings(); @@ -1229,8 +1228,8 @@ const MenuButton: React.FC = ({ } if (isGroupStatus && !!status.group) { - const isGroupOwner = groupRelationship?.role === GroupRoles.OWNER; - const isGroupAdmin = groupRelationship?.role === GroupRoles.ADMIN; + const isGroupOwner = group?.relationship?.role === GroupRoles.OWNER; + const isGroupAdmin = group?.relationship?.role === GroupRoles.ADMIN; // const isStatusFromOwner = group.owner.id === account.id; const canBanUser = match && (isGroupOwner || isGroupAdmin) && !ownAccount; diff --git a/packages/pl-fe/src/features/group/components/group-action-button.tsx b/packages/pl-fe/src/features/group/components/group-action-button.tsx index a10d35c27..671b2e34c 100644 --- a/packages/pl-fe/src/features/group/components/group-action-button.tsx +++ b/packages/pl-fe/src/features/group/components/group-action-button.tsx @@ -2,16 +2,15 @@ import { GroupRoles } from 'pl-api'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useJoinGroup } from '@/api/hooks/groups/use-join-group'; -import { useLeaveGroup } from '@/api/hooks/groups/use-leave-group'; import Button from '@/components/ui/button'; -import { importEntities } from '@/entity-store/actions'; -import { Entities } from '@/entity-store/entities'; -import { useAppDispatch } from '@/hooks/use-app-dispatch'; +import { + useJoinGroupMutation, + useLeaveGroupMutation, +} from '@/queries/groups/use-group-relationship'; import { useModalsActions } from '@/stores/modals'; import toast from '@/toast'; -import type { Group, GroupRelationship } from 'pl-api'; +import type { Group } from 'pl-api'; interface IGroupActionButton { group: Pick; @@ -33,12 +32,11 @@ const messages = defineMessages({ }); const GroupActionButton = ({ group }: IGroupActionButton) => { - const dispatch = useAppDispatch(); const intl = useIntl(); const { openModal } = useModalsActions(); - const joinGroup = useJoinGroup(group); - const leaveGroup = useLeaveGroup(group); + const { mutate: joinGroup, isPending: isJoiningGroup } = useJoinGroupMutation(group.id); + const { mutate: leaveGroup, isPending: isLeavingGroup } = useLeaveGroupMutation(group.id); const isRequested = group.relationship?.requested; const isNonMember = !group.relationship?.member && !isRequested; @@ -46,26 +44,21 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { const isAdmin = group.relationship?.role === GroupRoles.ADMIN; const onJoinGroup = () => - joinGroup.mutate( - {}, - { - onSuccess() { - joinGroup.invalidate(); - - toast.success( - group.locked - ? intl.formatMessage(messages.joinRequestSuccess) - : intl.formatMessage(messages.joinSuccess), - ); - }, - onError(error) { - const message = error.response?.json?.error; - if (message) { - toast.error(message); - } - }, + joinGroup(undefined, { + onSuccess: () => { + toast.success( + group.locked + ? intl.formatMessage(messages.joinRequestSuccess) + : intl.formatMessage(messages.joinSuccess), + ); }, - ); + // onError: (error) => { + // const message = error.response?.json?.error; + // if (message) { + // toast.error(message); + // } + // }, + }); const onLeaveGroup = () => { openModal('CONFIRM', { @@ -73,25 +66,15 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { message: intl.formatMessage(messages.confirmationMessage), confirm: intl.formatMessage(messages.confirmationConfirm), onConfirm: () => - leaveGroup.mutate(group.relationship?.id as string, { - onSuccess() { - leaveGroup.invalidate(); + leaveGroup(undefined, { + onSuccess: () => { toast.success(intl.formatMessage(messages.leaveSuccess)); }, }), }); }; - const onCancelRequest = () => - leaveGroup.mutate(group.relationship?.id as string, { - onSuccess() { - const entity = { - ...(group.relationship as GroupRelationship), - requested: false, - }; - dispatch(importEntities([entity], Entities.GROUP_RELATIONSHIPS)); - }, - }); + const onCancelRequest = () => leaveGroup(); if (isOwner || isAdmin) { return ( @@ -103,7 +86,7 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { if (isNonMember) { return ( - ); } return ( - ); diff --git a/packages/pl-fe/src/features/group/components/group-member-list-item.tsx b/packages/pl-fe/src/features/group/components/group-member-list-item.tsx index c283de365..43abae6ff 100644 --- a/packages/pl-fe/src/features/group/components/group-member-list-item.tsx +++ b/packages/pl-fe/src/features/group/components/group-member-list-item.tsx @@ -4,8 +4,6 @@ import React, { useMemo } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useAccount } from '@/api/hooks/accounts/use-account'; -import { useDemoteGroupMember } from '@/api/hooks/groups/use-demote-group-member'; -import { usePromoteGroupMember } from '@/api/hooks/groups/use-promote-group-member'; import Account from '@/components/account'; import DropdownMenu from '@/components/dropdown-menu/dropdown-menu'; import HStack from '@/components/ui/hstack'; @@ -15,7 +13,9 @@ import PlaceholderAccount from '@/features/placeholder/components/placeholder-ac import { useAppDispatch } from '@/hooks/use-app-dispatch'; import { useBlockGroupUserMutation } from '@/queries/groups/use-group-blocks'; import { + useDemoteGroupMemberMutation, useKickGroupMemberMutation, + usePromoteGroupMemberMutation, type MinifiedGroupMember, } from '@/queries/groups/use-group-members'; import { useModalsActions } from '@/stores/modals'; @@ -79,8 +79,8 @@ const GroupMemberListItem = ({ member, group }: IGroupMemberListItem) => { const { mutate: blockGroupMember } = useBlockGroupUserMutation(group.id, member.account_id); const { mutate: kickGroupMember } = useKickGroupMemberMutation(group.id, member.account_id); - const promoteGroupMember = usePromoteGroupMember(group, member); - const demoteGroupMember = useDemoteGroupMember(group, member); + const { mutate: promoteGroupMember } = usePromoteGroupMemberMutation(group.id); + const { mutate: demoteGroupMember } = useDemoteGroupMemberMutation(group.id); const { account, isLoading } = useAccount(member.account_id); @@ -91,6 +91,7 @@ const GroupMemberListItem = ({ member, group }: IGroupMemberListItem) => { // Member role const isMemberOwner = member.role === GroupRoles.OWNER; const isMemberAdmin = member.role === GroupRoles.ADMIN; + // const isMemberModerator = membisMemberModeratorer.role === GroupRoles.MODERATOR; const isMemberUser = member.role === GroupRoles.USER; const handleKickFromGroup = () => { @@ -131,7 +132,7 @@ const GroupMemberListItem = ({ member, group }: IGroupMemberListItem) => { confirm: intl.formatMessage(messages.promoteConfirm), onConfirm: () => { promoteGroupMember( - { role: GroupRoles.ADMIN, account_ids: [member.account_id] }, + { accountId: member.account_id, role: GroupRoles.ADMIN }, { onSuccess() { toast.success(intl.formatMessage(messages.promotedToAdmin, { name: account?.acct })); @@ -144,7 +145,7 @@ const GroupMemberListItem = ({ member, group }: IGroupMemberListItem) => { const handleUserAssignment = () => { demoteGroupMember( - { role: GroupRoles.USER, account_ids: [member.account_id] }, + { accountId: member.account_id, role: GroupRoles.USER }, { onSuccess() { toast.success(intl.formatMessage(messages.demotedToUser, { name: account?.acct })); diff --git a/packages/pl-fe/src/features/group/components/group-options-button.tsx b/packages/pl-fe/src/features/group/components/group-options-button.tsx index 1f67cfa39..ecf0fe421 100644 --- a/packages/pl-fe/src/features/group/components/group-options-button.tsx +++ b/packages/pl-fe/src/features/group/components/group-options-button.tsx @@ -2,9 +2,9 @@ import { GroupRoles, type Group } from 'pl-api'; import React, { useMemo } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useLeaveGroup } from '@/api/hooks/groups/use-leave-group'; import DropdownMenu, { Menu } from '@/components/dropdown-menu'; import IconButton from '@/components/ui/icon-button'; +import { useLeaveGroupMutation } from '@/queries/groups/use-group-relationship'; import { useModalsActions } from '@/stores/modals'; import toast from '@/toast'; @@ -28,7 +28,7 @@ const GroupOptionsButton = ({ group }: IGroupActionButton) => { const { openModal } = useModalsActions(); const intl = useIntl(); - const leaveGroup = useLeaveGroup(group); + const { mutate: leaveGroup } = useLeaveGroupMutation(group.id); const isMember = group.relationship?.role === GroupRoles.USER; const isAdmin = group.relationship?.role === GroupRoles.ADMIN; @@ -51,9 +51,8 @@ const GroupOptionsButton = ({ group }: IGroupActionButton) => { message: intl.formatMessage(messages.confirmationMessage), confirm: intl.formatMessage(messages.confirmationConfirm), onConfirm: () => - leaveGroup.mutate(group.relationship?.id as string, { - onSuccess() { - leaveGroup.invalidate(); + leaveGroup(undefined, { + onSuccess: () => { toast.success(intl.formatMessage(messages.leaveSuccess)); }, }), diff --git a/packages/pl-fe/src/features/groups/components/discover/group-list-item.tsx b/packages/pl-fe/src/features/groups/components/discover/group-list-item.tsx index 82ba85339..c099eaa84 100644 --- a/packages/pl-fe/src/features/groups/components/discover/group-list-item.tsx +++ b/packages/pl-fe/src/features/groups/components/discover/group-list-item.tsx @@ -9,27 +9,18 @@ import Stack from '@/components/ui/stack'; import Text from '@/components/ui/text'; import Emojify from '@/features/emoji/emojify'; import GroupActionButton from '@/features/group/components/group-action-button'; +import { useGroupQuery } from '@/queries/groups/use-group'; import { shortNumberFormat } from '@/utils/numbers'; -import type { Group } from 'pl-api'; - interface IGroupListItem { - group: Pick< - Group, - | 'id' - | 'avatar' - | 'avatar_description' - | 'display_name' - | 'emojis' - | 'locked' - | 'members_count' - | 'relationship' - >; + groupId: string; withJoinAction?: boolean; } -const GroupListItem = (props: IGroupListItem) => { - const { group, withJoinAction = true } = props; +const GroupListItem: React.FC = ({ groupId, withJoinAction = true }) => { + const { data: group } = useGroupQuery(groupId, true); + + if (!group) return null; return ( diff --git a/packages/pl-fe/src/features/ui/components/compose-button.tsx b/packages/pl-fe/src/features/ui/components/compose-button.tsx index 951a3bb55..3748e352a 100644 --- a/packages/pl-fe/src/features/ui/components/compose-button.tsx +++ b/packages/pl-fe/src/features/ui/components/compose-button.tsx @@ -3,11 +3,11 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { groupComposeModal } from '@/actions/compose'; -import { useGroup } from '@/api/hooks/groups/use-group'; import Avatar from '@/components/ui/avatar'; import HStack from '@/components/ui/hstack'; import Icon from '@/components/ui/icon'; import { useAppDispatch } from '@/hooks/use-app-dispatch'; +import { useGroupQuery } from '@/queries/groups/use-group'; import { useModalsActions } from '@/stores/modals'; import { layouts } from '../router'; @@ -19,7 +19,7 @@ interface IComposeButton { const ComposeButton: React.FC = ({ shrink }) => { const match = useMatch({ from: layouts.group.id, shouldThrow: false }); - const { group } = useGroup(match?.params.groupId ?? ''); + const { data: group } = useGroupQuery(match?.params.groupId); const isGroupMember = !!group?.relationship?.member; if (match && isGroupMember) { @@ -49,7 +49,7 @@ const HomeComposeButton: React.FC = ({ shrink }) => { const GroupComposeButton: React.FC = ({ shrink }) => { const dispatch = useAppDispatch(); const match = useMatch({ from: layouts.group.id, shouldThrow: false }); - const { group } = useGroup(match?.params.groupId ?? ''); + const { data: group } = useGroupQuery(match?.params.groupId); if (!group) return null; diff --git a/packages/pl-fe/src/features/ui/components/panels/my-groups-panel.tsx b/packages/pl-fe/src/features/ui/components/panels/my-groups-panel.tsx index 7d7c59df0..ead19bba7 100644 --- a/packages/pl-fe/src/features/ui/components/panels/my-groups-panel.tsx +++ b/packages/pl-fe/src/features/ui/components/panels/my-groups-panel.tsx @@ -1,14 +1,14 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; -import { useGroups } from '@/api/hooks/groups/use-groups'; import Widget from '@/components/ui/widget'; import GroupListItem from '@/features/groups/components/discover/group-list-item'; import PlaceholderGroupSearch from '@/features/placeholder/components/placeholder-group-search'; +import { useGroupsQuery } from '@/queries/groups/use-groups'; const MyGroupsPanel = () => { - const { groups, isFetching, isFetched, isError } = useGroups(); - const isEmpty = (isFetched && groups.length === 0) ?? isError; + const { data: groupIds = [], isFetching, isError } = useGroupsQuery(); + const isEmpty = (!isFetching && groupIds.length === 0) ?? isError; if (isEmpty) { return null; @@ -20,9 +20,11 @@ const MyGroupsPanel = () => { ? new Array(3) .fill(0) .map((_, idx) => ) - : groups + : groupIds .slice(0, 3) - .map((group) => )} + .map((groupId) => ( + + ))} ); }; diff --git a/packages/pl-fe/src/layouts/group-layout.tsx b/packages/pl-fe/src/layouts/group-layout.tsx index 392cfa679..7484128db 100644 --- a/packages/pl-fe/src/layouts/group-layout.tsx +++ b/packages/pl-fe/src/layouts/group-layout.tsx @@ -2,8 +2,6 @@ import { Outlet, useLocation } from '@tanstack/react-router'; import React, { useMemo } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; -import { useGroup } from '@/api/hooks/groups/use-group'; -import { useGroupMembershipRequests } from '@/api/hooks/groups/use-group-membership-requests'; import Column from '@/components/ui/column'; import Icon from '@/components/ui/icon'; import Layout from '@/components/ui/layout'; @@ -15,6 +13,8 @@ import LinkFooter from '@/features/ui/components/link-footer'; import { layouts } from '@/features/ui/router'; import { GroupMediaPanel, SignUpPanel } from '@/features/ui/util/async-components'; import { useOwnAccount } from '@/hooks/use-own-account'; +import { useGroupQuery } from '@/queries/groups/use-group'; +import { useGroupMembershipRequestsQuery } from '@/queries/groups/use-group-members'; const messages = defineMessages({ all: { id: 'group.tabs.all', defaultMessage: 'All' }, @@ -48,8 +48,8 @@ const GroupLayout = () => { const location = useLocation(); const { account: me } = useOwnAccount(); - const { group } = useGroup(groupId); - const { accounts: pending } = useGroupMembershipRequests(groupId); + const { data: group } = useGroupQuery(groupId, true); + const { data: membershipRequests = [] } = useGroupMembershipRequestsQuery(groupId); const isMember = !!group?.relationship?.member; const isPrivate = group?.locked; @@ -76,12 +76,12 @@ const GroupLayout = () => { to: '/groups/$groupId/members', params: { groupId }, name: '/groups/$groupId/members', - count: pending.length, + count: membershipRequests.length, }, ); return items; - }, [pending.length, groupId]); + }, [membershipRequests.length, groupId]); const renderChildren = () => { if (!isMember && isPrivate) { diff --git a/packages/pl-fe/src/pages/groups/edit-group.tsx b/packages/pl-fe/src/pages/groups/edit-group.tsx index 95d081a65..fc5043371 100644 --- a/packages/pl-fe/src/pages/groups/edit-group.tsx +++ b/packages/pl-fe/src/pages/groups/edit-group.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { useGroup } from '@/api/hooks/groups/use-group'; import { useUpdateGroup } from '@/api/hooks/groups/use-update-group'; import Button from '@/components/ui/button'; import Column from '@/components/ui/column'; @@ -19,9 +18,9 @@ import { useImageField } from '@/hooks/forms/use-image-field'; import { useTextField } from '@/hooks/forms/use-text-field'; import { useAppSelector } from '@/hooks/use-app-selector'; import { useInstance } from '@/hooks/use-instance'; +import { useGroupQuery } from '@/queries/groups/use-group'; import toast from '@/toast'; import { unescapeHTML } from '@/utils/html'; - const messages = defineMessages({ heading: { id: 'navigation_bar.edit_group', defaultMessage: 'Edit Group' }, groupNamePlaceholder: { @@ -41,7 +40,7 @@ const EditGroup: React.FC = () => { const intl = useIntl(); const instance = useInstance(); - const { group, isLoading } = useGroup(groupId); + const { data: group, isLoading } = useGroupQuery(groupId); const { updateGroup } = useUpdateGroup(groupId); const [isSubmitting, setIsSubmitting] = useState(false); diff --git a/packages/pl-fe/src/pages/groups/group-blocked-members.tsx b/packages/pl-fe/src/pages/groups/group-blocked-members.tsx index e570ac27b..d3dd35941 100644 --- a/packages/pl-fe/src/pages/groups/group-blocked-members.tsx +++ b/packages/pl-fe/src/pages/groups/group-blocked-members.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { useAccount } from '@/api/hooks/accounts/use-account'; -import { useGroup } from '@/api/hooks/groups/use-group'; import Account from '@/components/account'; import ScrollableList from '@/components/scrollable-list'; import Button from '@/components/ui/button'; @@ -11,6 +10,7 @@ import HStack from '@/components/ui/hstack'; import Spinner from '@/components/ui/spinner'; import ColumnForbidden from '@/features/ui/components/column-forbidden'; import { groupBlocksRoute } from '@/features/ui/router'; +import { useGroupQuery } from '@/queries/groups/use-group'; import { useGroupBlocks, useUnblockGroupUserMutation } from '@/queries/groups/use-group-blocks'; import toast from '@/toast'; @@ -64,7 +64,7 @@ const GroupBlockedMembers: React.FC = () => { const intl = useIntl(); - const { group } = useGroup(groupId); + const { data: group } = useGroupQuery(groupId, true); const { data: accountIds } = useGroupBlocks(groupId); if (!group || !group.relationship || !accountIds) { diff --git a/packages/pl-fe/src/pages/groups/group-gallery.tsx b/packages/pl-fe/src/pages/groups/group-gallery.tsx index f248c11f9..fee99516f 100644 --- a/packages/pl-fe/src/pages/groups/group-gallery.tsx +++ b/packages/pl-fe/src/pages/groups/group-gallery.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; -import { useGroup } from '@/api/hooks/groups/use-group'; import LoadMore from '@/components/load-more'; import MissingIndicator from '@/components/missing-indicator'; import Column from '@/components/ui/column'; @@ -9,6 +8,7 @@ import Spinner from '@/components/ui/spinner'; import { groupGalleryRoute } from '@/features/ui/router'; import { type AccountGalleryAttachment, useGroupGallery } from '@/hooks/use-account-gallery'; import { MediaItem } from '@/pages/accounts/account-gallery'; +import { useGroupQuery } from '@/queries/groups/use-group'; import { useModalsActions } from '@/stores/modals'; const GroupGallery: React.FC = () => { @@ -16,7 +16,7 @@ const GroupGallery: React.FC = () => { const { openModal } = useModalsActions(); - const { group, isLoading: groupIsLoading } = useGroup(groupId); + const { data: group, isLoading: groupIsLoading } = useGroupQuery(groupId, true); const { data: attachments, diff --git a/packages/pl-fe/src/pages/groups/group-members.tsx b/packages/pl-fe/src/pages/groups/group-members.tsx index 6c85a1eb7..0b2129a29 100644 --- a/packages/pl-fe/src/pages/groups/group-members.tsx +++ b/packages/pl-fe/src/pages/groups/group-members.tsx @@ -2,19 +2,21 @@ import clsx from 'clsx'; import { GroupRoles } from 'pl-api'; import React, { useMemo } from 'react'; -import { useGroup } from '@/api/hooks/groups/use-group'; -import { useGroupMembershipRequests } from '@/api/hooks/groups/use-group-membership-requests'; import { PendingItemsRow } from '@/components/pending-items-row'; import ScrollableList from '@/components/scrollable-list'; import GroupMemberListItem from '@/features/group/components/group-member-list-item'; import PlaceholderAccount from '@/features/placeholder/components/placeholder-account'; import { groupMembersRoute } from '@/features/ui/router'; -import { useGroupMembers } from '@/queries/groups/use-group-members'; +import { useGroupQuery } from '@/queries/groups/use-group'; +import { + useGroupMembers, + useGroupMembershipRequestsQuery, +} from '@/queries/groups/use-group-members'; const GroupMembers: React.FC = () => { const { groupId } = groupMembersRoute.useParams(); - const { group, isFetching: isFetchingGroup } = useGroup(groupId); + const { data: group, isFetching: isFetchingGroup } = useGroupQuery(groupId, true); const { data: owners, isFetching: isFetchingOwners } = useGroupMembers(groupId, GroupRoles.OWNER); const { data: admins, isFetching: isFetchingAdmins } = useGroupMembers(groupId, GroupRoles.ADMIN); const { @@ -23,8 +25,8 @@ const GroupMembers: React.FC = () => { fetchNextPage, hasNextPage, } = useGroupMembers(groupId, GroupRoles.USER); - const { isFetching: isFetchingPending, count: pendingCount } = - useGroupMembershipRequests(groupId); + const { isFetching: isFetchingPending, data: membershipRequests = [] } = + useGroupMembershipRequestsQuery(groupId); const isLoading = isFetchingGroup || isFetchingOwners || isFetchingAdmins || isFetchingUsers || isFetchingPending; @@ -47,7 +49,7 @@ const GroupMembers: React.FC = () => { className='⁂-status-list' itemClassName='py-3 last:pb-0' prepend={ - pendingCount > 0 && ( + membershipRequests.length > 0 && (
{ >
) diff --git a/packages/pl-fe/src/pages/groups/group-membership-requests.tsx b/packages/pl-fe/src/pages/groups/group-membership-requests.tsx index 286439ee5..251abe881 100644 --- a/packages/pl-fe/src/pages/groups/group-membership-requests.tsx +++ b/packages/pl-fe/src/pages/groups/group-membership-requests.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; -import { useGroup } from '@/api/hooks/groups/use-group'; -import { useGroupMembershipRequests } from '@/api/hooks/groups/use-group-membership-requests'; +import { useAccount } from '@/api/hooks/accounts/use-account'; import Account from '@/components/account'; import { AuthorizeRejectButtons } from '@/components/authorize-reject-buttons'; import ScrollableList from '@/components/scrollable-list'; @@ -11,6 +10,12 @@ import HStack from '@/components/ui/hstack'; import Spinner from '@/components/ui/spinner'; import ColumnForbidden from '@/features/ui/components/column-forbidden'; import { groupMembershipRequestsRoute } from '@/features/ui/router'; +import { useGroupQuery } from '@/queries/groups/use-group'; +import { + useAcceptGroupMembershipRequestMutation, + useGroupMembershipRequestsQuery, + useRejectGroupMembershipRequestMutation, +} from '@/queries/groups/use-group-members'; import toast from '@/toast'; import type { PlfeResponse } from '@/api'; @@ -26,12 +31,14 @@ const messages = defineMessages({ }); interface IMembershipRequest { - account: AccountEntity; + accountId: string; onAuthorize(account: AccountEntity): Promise; onReject(account: AccountEntity): Promise; } -const MembershipRequest: React.FC = ({ account, onAuthorize, onReject }) => { +const MembershipRequest: React.FC = ({ accountId, onAuthorize, onReject }) => { + const { account } = useAccount(); + if (!account) return null; const handleAuthorize = () => onAuthorize(account); @@ -57,11 +64,13 @@ const GroupMembershipRequests: React.FC = () => { const intl = useIntl(); - const { group } = useGroup(groupId); + const { data: group } = useGroupQuery(groupId, true); - const { accounts, authorize, reject, refetch, isLoading } = useGroupMembershipRequests(groupId); + const { data: accountIds = [], isFetching } = useGroupMembershipRequestsQuery(groupId); + const { mutate: acceptGroupMembershipRequest } = useAcceptGroupMembershipRequestMutation(groupId); + const { mutate: rejectGroupMembershipRequest } = useRejectGroupMembershipRequestMutation(groupId); - if (!group || !group.relationship || isLoading) { + if (!group || !group.relationship || isFetching) { return ( @@ -76,39 +85,41 @@ const GroupMembershipRequests: React.FC = () => { return ; } - const handleAuthorize = async (account: AccountEntity) => { - try { - await authorize(account.id); - } catch (error) { - const { response } = error as { response: PlfeResponse }; + const handleAuthorize = (account: AccountEntity) => + new Promise((resolve, reject) => { + acceptGroupMembershipRequest(account.id, { + onSuccess: () => resolve(), + onError: (error) => { + const { response } = error as unknown as { response: PlfeResponse }; - refetch(); + let message = intl.formatMessage(messages.authorizeFail, { name: account.username }); + if (response?.status === 409) { + message = response.json.error; + } + toast.error(message); - let message = intl.formatMessage(messages.authorizeFail, { name: account.username }); - if (response?.status === 409) { - message = response.json.error; - } - toast.error(message); - } - }; + reject(); + }, + }); + }); - const handleReject = async (account: AccountEntity) => { - try { - await reject(account.id); - } catch (error) { - const { response } = error as { response: PlfeResponse }; + const handleReject = (account: AccountEntity) => + new Promise((resolve, reject) => { + rejectGroupMembershipRequest(account.id, { + onSuccess: () => resolve(), + onError: (error) => { + const { response } = error as unknown as { response: PlfeResponse }; - refetch(); + let message = intl.formatMessage(messages.rejectFail, { name: account.username }); + if (response?.status === 409) { + message = response.json.error; + } + toast.error(message); - let message = intl.formatMessage(messages.rejectFail, { name: account.username }); - if (response?.status === 409) { - message = response.json.error; - } - toast.error(message); - - return Promise.reject(); - } - }; + reject(); + }, + }); + }); return ( @@ -121,10 +132,10 @@ const GroupMembershipRequests: React.FC = () => { /> } > - {accounts.map((account) => ( + {accountIds.map((account) => ( diff --git a/packages/pl-fe/src/pages/groups/groups.tsx b/packages/pl-fe/src/pages/groups/groups.tsx index e072f314b..5dbb84032 100644 --- a/packages/pl-fe/src/pages/groups/groups.tsx +++ b/packages/pl-fe/src/pages/groups/groups.tsx @@ -2,25 +2,19 @@ import { Link } from '@tanstack/react-router'; import React from 'react'; import { FormattedMessage } from 'react-intl'; -import { useGroups } from '@/api/hooks/groups/use-groups'; import GroupCard from '@/components/group-card'; import ScrollableList from '@/components/scrollable-list'; import Button from '@/components/ui/button'; import Stack from '@/components/ui/stack'; import Text from '@/components/ui/text'; import PlaceholderGroupCard from '@/features/placeholder/components/placeholder-group-card'; +import { useGroupsQuery } from '@/queries/groups/use-groups'; import { useModalsActions } from '@/stores/modals'; const Groups: React.FC = () => { const { openModal } = useModalsActions(); - const { groups, isLoading, hasNextPage, fetchNextPage } = useGroups(); - - const handleLoadMore = () => { - if (hasNextPage) { - fetchNextPage(); - } - }; + const { data: groupIds = [], isFetching, isLoading } = useGroupsQuery(); const createGroup = () => { openModal('CREATE_GROUP'); @@ -49,7 +43,7 @@ const Groups: React.FC = () => { return ( - {!(!isLoading && groups.length === 0) && ( + {!(!isFetching && groupIds.length === 0) && (