diff --git a/app/soapbox/features/group/components/group-action-button.tsx b/app/soapbox/features/group/components/group-action-button.tsx index 8c8a36db7..c697bc4ee 100644 --- a/app/soapbox/features/group/components/group-action-button.tsx +++ b/app/soapbox/features/group/components/group-action-button.tsx @@ -40,6 +40,8 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { const onJoinGroup = () => joinGroup.mutate({}, { onSuccess() { + joinGroup.invalidate(); + toast.success( group.locked ? intl.formatMessage(messages.joinRequestSuccess) @@ -53,8 +55,9 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { heading: intl.formatMessage(messages.confirmationHeading), message: intl.formatMessage(messages.confirmationMessage), confirm: intl.formatMessage(messages.confirmationConfirm), - onConfirm: () => leaveGroup.mutate({}, { + onConfirm: () => leaveGroup.mutate(group.relationship?.id as string, { onSuccess() { + leaveGroup.invalidate(); toast.success(intl.formatMessage(messages.leaveSuccess)); }, }), diff --git a/app/soapbox/features/groups/components/discover/search/results.tsx b/app/soapbox/features/groups/components/discover/search/results.tsx index 72bca24ad..14e1e5a67 100644 --- a/app/soapbox/features/groups/components/discover/search/results.tsx +++ b/app/soapbox/features/groups/components/discover/search/results.tsx @@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl'; import { Components, Virtuoso, VirtuosoGrid } from 'react-virtuoso'; import { HStack, Icon, Stack, Text } from 'soapbox/components/ui'; -import { useGroupSearch } from 'soapbox/queries/groups/search'; +import { useGroupSearch } from 'soapbox/hooks/api'; import { Group } from 'soapbox/types/entities'; import GroupGridItem from '../group-grid-item'; diff --git a/app/soapbox/features/groups/components/discover/search/search.tsx b/app/soapbox/features/groups/components/discover/search/search.tsx index ef8605435..4e3308353 100644 --- a/app/soapbox/features/groups/components/discover/search/search.tsx +++ b/app/soapbox/features/groups/components/discover/search/search.tsx @@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl'; import { Stack } from 'soapbox/components/ui'; import PlaceholderGroupSearch from 'soapbox/features/placeholder/components/placeholder-group-search'; import { useDebounce, useOwnAccount } from 'soapbox/hooks'; -import { useGroupSearch } from 'soapbox/queries/groups/search'; +import { useGroupSearch } from 'soapbox/hooks/api'; import { saveGroupSearch } from 'soapbox/utils/groups'; import Blankslate from './blankslate'; diff --git a/app/soapbox/hooks/api/groups/useGroupSearch.ts b/app/soapbox/hooks/api/groups/useGroupSearch.ts new file mode 100644 index 000000000..17c10e90f --- /dev/null +++ b/app/soapbox/hooks/api/groups/useGroupSearch.ts @@ -0,0 +1,39 @@ +import { Entities } from 'soapbox/entity-store/entities'; +import { useEntities } from 'soapbox/entity-store/hooks'; +import { groupSchema } from 'soapbox/schemas'; + +import { useApi } from '../../useApi'; +import { useFeatures } from '../../useFeatures'; + +import { useGroupRelationships } from './useGroups'; + +import type { Group } from 'soapbox/schemas'; + +function useGroupSearch(search: string) { + const api = useApi(); + const features = useFeatures(); + + const { entities, ...result } = useEntities( + [Entities.GROUPS, 'discover', 'search', search], + () => api.get('/api/v1/groups/search', { + params: { + q: search, + }, + }), + { enabled: features.groupsDiscovery && !!search, schema: groupSchema }, + ); + + const { relationships } = useGroupRelationships(entities.map(entity => entity.id)); + + const groups = entities.map((group) => ({ + ...group, + relationship: relationships[group.id] || null, + })); + + return { + ...result, + groups, + }; +} + +export { useGroupSearch }; \ No newline at end of file diff --git a/app/soapbox/hooks/api/groups/useJoinGroup.ts b/app/soapbox/hooks/api/groups/useJoinGroup.ts index f30a9c25b..46cd1d5bd 100644 --- a/app/soapbox/hooks/api/groups/useJoinGroup.ts +++ b/app/soapbox/hooks/api/groups/useJoinGroup.ts @@ -2,9 +2,13 @@ import { Entities } from 'soapbox/entity-store/entities'; import { useEntityActions } from 'soapbox/entity-store/hooks'; import { groupRelationshipSchema } from 'soapbox/schemas'; +import { useGroups } from './useGroups'; + import type { Group, GroupRelationship } from 'soapbox/schemas'; function useJoinGroup(group: Group) { + const { invalidate } = useGroups(); + const { createEntity, isLoading } = useEntityActions( [Entities.GROUP_RELATIONSHIPS, group.id], { post: `/api/v1/groups/${group.id}/join` }, @@ -14,6 +18,7 @@ function useJoinGroup(group: Group) { return { mutate: createEntity, isLoading, + invalidate, }; } diff --git a/app/soapbox/hooks/api/groups/useLeaveGroup.ts b/app/soapbox/hooks/api/groups/useLeaveGroup.ts index 1f6b99f8d..af78c6d35 100644 --- a/app/soapbox/hooks/api/groups/useLeaveGroup.ts +++ b/app/soapbox/hooks/api/groups/useLeaveGroup.ts @@ -1,8 +1,14 @@ import { Entities } from 'soapbox/entity-store/entities'; import { useEntityActions } from 'soapbox/entity-store/hooks'; -import { Group, GroupRelationship, groupRelationshipSchema } from 'soapbox/schemas'; +import { groupRelationshipSchema } from 'soapbox/schemas'; + +import { useGroups } from './useGroups'; + +import type { Group, GroupRelationship } from 'soapbox/schemas'; function useLeaveGroup(group: Group) { + const { invalidate } = useGroups(); + const { createEntity, isLoading } = useEntityActions( [Entities.GROUP_RELATIONSHIPS, group.id], { post: `/api/v1/groups/${group.id}/leave` }, @@ -12,6 +18,7 @@ function useLeaveGroup(group: Group) { return { mutate: createEntity, isLoading, + invalidate, }; } diff --git a/app/soapbox/hooks/api/index.ts b/app/soapbox/hooks/api/index.ts index d1c8ec09f..b70314633 100644 --- a/app/soapbox/hooks/api/index.ts +++ b/app/soapbox/hooks/api/index.ts @@ -6,6 +6,7 @@ export { useCancelMembershipRequest } from './groups/useCancelMembershipRequest' export { useDeleteGroup } from './groups/useDeleteGroup'; export { useDemoteGroupMember } from './groups/useDemoteGroupMember'; export { useGroup, useGroups } from './groups/useGroups'; +export { useGroupSearch } from './groups/useGroupSearch'; export { useJoinGroup } from './groups/useJoinGroup'; export { useLeaveGroup } from './groups/useLeaveGroup'; export { usePromoteGroupMember } from './groups/usePromoteGroupMember'; diff --git a/app/soapbox/normalizers/group-relationship.ts b/app/soapbox/normalizers/group-relationship.ts index c9326db91..4ac579e6d 100644 --- a/app/soapbox/normalizers/group-relationship.ts +++ b/app/soapbox/normalizers/group-relationship.ts @@ -8,13 +8,15 @@ import { fromJS, } from 'immutable'; +import { GroupRoles } from 'soapbox/schemas/group-member'; + export const GroupRelationshipRecord = ImmutableRecord({ id: '', blocked_by: false, member: false, notifying: null, requested: false, - role: null as 'admin' | 'moderator' | 'user' | null, + role: 'user' as GroupRoles, }); export const normalizeGroupRelationship = (relationship: Record) => { diff --git a/app/soapbox/schemas/group-relationship.ts b/app/soapbox/schemas/group-relationship.ts index 9f6eaaf35..75584955e 100644 --- a/app/soapbox/schemas/group-relationship.ts +++ b/app/soapbox/schemas/group-relationship.ts @@ -1,10 +1,12 @@ import z from 'zod'; +import { GroupRoles } from './group-member'; + const groupRelationshipSchema = z.object({ id: z.string(), member: z.boolean().catch(false), requested: z.boolean().catch(false), - role: z.string().nullish().catch(null), + role: z.nativeEnum(GroupRoles).catch(GroupRoles.USER), blocked_by: z.boolean().catch(false), notifying: z.boolean().nullable().catch(null), });