From 7fffe59fb9d3ca93ac7b5a164b5aba38e23592b1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 15 Mar 2023 17:19:42 -0500 Subject: [PATCH 1/7] Allow "owner" permissions on group pages --- .../features/group/components/group-action-button.tsx | 2 +- app/soapbox/features/group/group-blocked-members.tsx | 2 +- app/soapbox/features/group/group-membership-requests.tsx | 2 +- app/soapbox/features/group/manage-group.tsx | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/soapbox/features/group/components/group-action-button.tsx b/app/soapbox/features/group/components/group-action-button.tsx index 32ff557af..64824866a 100644 --- a/app/soapbox/features/group/components/group-action-button.tsx +++ b/app/soapbox/features/group/components/group-action-button.tsx @@ -27,7 +27,7 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { const isRequested = group.relationship?.requested; const isNonMember = !group.relationship?.member && !isRequested; - const isAdmin = group.relationship?.role === 'admin'; + const isAdmin = group.relationship?.role === 'owner'; const isBlocked = group.relationship?.blocked_by; const onJoinGroup = () => joinGroup.mutate(group); diff --git a/app/soapbox/features/group/group-blocked-members.tsx b/app/soapbox/features/group/group-blocked-members.tsx index 7af25f99c..aae717c12 100644 --- a/app/soapbox/features/group/group-blocked-members.tsx +++ b/app/soapbox/features/group/group-blocked-members.tsx @@ -81,7 +81,7 @@ const GroupBlockedMembers: React.FC = ({ params }) => { ); } - if (!group.relationship.role || !['admin', 'moderator'].includes(group.relationship.role)) { + if (!group.relationship.role || !['owner', 'admin', 'moderator'].includes(group.relationship.role)) { return (); } diff --git a/app/soapbox/features/group/group-membership-requests.tsx b/app/soapbox/features/group/group-membership-requests.tsx index 8cf0fdfd7..13f0925ac 100644 --- a/app/soapbox/features/group/group-membership-requests.tsx +++ b/app/soapbox/features/group/group-membership-requests.tsx @@ -96,7 +96,7 @@ const GroupMembershipRequests: React.FC = ({ params }) ); } - if (!group.relationship.role || !['admin', 'moderator'].includes(group.relationship.role)) { + if (!group.relationship.role || !['owner', 'admin', 'moderator'].includes(group.relationship.role)) { return (); } diff --git a/app/soapbox/features/group/manage-group.tsx b/app/soapbox/features/group/manage-group.tsx index 918aea874..1ebdc7848 100644 --- a/app/soapbox/features/group/manage-group.tsx +++ b/app/soapbox/features/group/manage-group.tsx @@ -50,7 +50,7 @@ const ManageGroup: React.FC = ({ params }) => { ); } - if (!group.relationship.role || !['admin', 'moderator'].includes(group.relationship.role)) { + if (!group.relationship.role || !['owner', 'admin', 'moderator'].includes(group.relationship.role)) { return (); } @@ -72,7 +72,7 @@ const ManageGroup: React.FC = ({ params }) => { return ( - {group.relationship.role === 'admin' && ( + {group.relationship.role === 'owner' && ( @@ -83,7 +83,7 @@ const ManageGroup: React.FC = ({ params }) => { - {group.relationship.role === 'admin' && ( + {group.relationship.role === 'owner' && ( From 5871abd786f6ccc7bd4b4ea97efe643491fab804 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 15 Mar 2023 17:59:37 -0500 Subject: [PATCH 2/7] Make "Manage Group" use the EntityStore --- app/soapbox/features/group/manage-group.tsx | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/app/soapbox/features/group/manage-group.tsx b/app/soapbox/features/group/manage-group.tsx index 1ebdc7848..e7dea7f20 100644 --- a/app/soapbox/features/group/manage-group.tsx +++ b/app/soapbox/features/group/manage-group.tsx @@ -1,13 +1,12 @@ -import React, { useCallback, useEffect } from 'react'; +import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; -import { deleteGroup, editGroup, fetchGroup } from 'soapbox/actions/groups'; +import { deleteGroup, editGroup } from 'soapbox/actions/groups'; import { openModal } from 'soapbox/actions/modals'; import List, { ListItem } from 'soapbox/components/list'; import { CardBody, Column, Spinner } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import { makeGetGroup } from 'soapbox/selectors'; +import { useAppDispatch, useGroup } from 'soapbox/hooks'; import ColumnForbidden from '../ui/components/column-forbidden'; @@ -29,18 +28,12 @@ interface IManageGroup { } const ManageGroup: React.FC = ({ params }) => { - const history = useHistory(); + const { id } = params; const intl = useIntl(); + const history = useHistory(); const dispatch = useAppDispatch(); - const id = params?.id || ''; - - const getGroup = useCallback(makeGetGroup(), []); - const group = useAppSelector(state => getGroup(state, id)); - - useEffect(() => { - if (!group) dispatch(fetchGroup(id)); - }, [id]); + const { group } = useGroup(id); if (!group || !group.relationship) { return ( From 1518e88904338f92062fb6d7c2b662ae3981bb26 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 15 Mar 2023 18:37:50 -0500 Subject: [PATCH 3/7] Retrofit old Group actions to EntityStore --- app/soapbox/actions/groups.ts | 3 ++- app/soapbox/actions/importer/index.ts | 25 ++++++++++--------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/app/soapbox/actions/groups.ts b/app/soapbox/actions/groups.ts index b97d52f20..d78e7f5d8 100644 --- a/app/soapbox/actions/groups.ts +++ b/app/soapbox/actions/groups.ts @@ -1,5 +1,6 @@ import { defineMessages } from 'react-intl'; +import { deleteEntities } from 'soapbox/entity-store/actions'; import toast from 'soapbox/toast'; import api, { getLinks } from '../api'; @@ -191,7 +192,7 @@ const updateGroupFail = (error: AxiosError) => ({ }); const deleteGroup = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { - dispatch(deleteGroupRequest(id)); + dispatch(deleteEntities([id], 'Group')); return api(getState).delete(`/api/v1/groups/${id}`) .then(() => dispatch(deleteGroupSuccess(id))) diff --git a/app/soapbox/actions/importer/index.ts b/app/soapbox/actions/importer/index.ts index 8750a5d61..8d577cd49 100644 --- a/app/soapbox/actions/importer/index.ts +++ b/app/soapbox/actions/importer/index.ts @@ -1,3 +1,7 @@ +import { importEntities } from 'soapbox/entity-store/actions'; +import { Group, groupSchema } from 'soapbox/schemas'; +import { filteredArray } from 'soapbox/schemas/utils'; + import { getSettings } from '../settings'; import type { AppDispatch, RootState } from 'soapbox/store'; @@ -18,11 +22,11 @@ const importAccount = (account: APIEntity) => const importAccounts = (accounts: APIEntity[]) => ({ type: ACCOUNTS_IMPORT, accounts }); -const importGroup = (group: APIEntity) => - ({ type: GROUP_IMPORT, group }); +const importGroup = (group: Group) => + importEntities([group], 'Group'); -const importGroups = (groups: APIEntity[]) => - ({ type: GROUPS_IMPORT, groups }); +const importGroups = (groups: Group[]) => + importEntities(groups, 'Group'); const importStatus = (status: APIEntity, idempotencyKey?: string) => (dispatch: AppDispatch, getState: () => RootState) => { @@ -69,17 +73,8 @@ const importFetchedGroup = (group: APIEntity) => importFetchedGroups([group]); const importFetchedGroups = (groups: APIEntity[]) => { - const normalGroups: APIEntity[] = []; - - const processGroup = (group: APIEntity) => { - if (!group.id) return; - - normalGroups.push(group); - }; - - groups.forEach(processGroup); - - return importGroups(normalGroups); + const entities = filteredArray(groupSchema).catch([]).parse(groups); + return importGroups(entities); }; const importFetchedStatus = (status: APIEntity, idempotencyKey?: string) => From c51870af6e6e04c4f1b18d59939b83f5db067da5 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 15 Mar 2023 19:26:37 -0500 Subject: [PATCH 4/7] Update some more groups stuff to use entities --- app/soapbox/features/group/group-blocked-members.tsx | 12 +++++------- .../features/group/group-membership-requests.tsx | 12 +++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/soapbox/features/group/group-blocked-members.tsx b/app/soapbox/features/group/group-blocked-members.tsx index aae717c12..9c197da62 100644 --- a/app/soapbox/features/group/group-blocked-members.tsx +++ b/app/soapbox/features/group/group-blocked-members.tsx @@ -1,12 +1,12 @@ import React, { useCallback, useEffect } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; -import { fetchGroup, fetchGroupBlocks, groupUnblock } from 'soapbox/actions/groups'; +import { fetchGroupBlocks, groupUnblock } from 'soapbox/actions/groups'; import Account from 'soapbox/components/account'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Button, Column, HStack, Spinner } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount, makeGetGroup } from 'soapbox/selectors'; +import { useAppDispatch, useAppSelector, useGroup } from 'soapbox/hooks'; +import { makeGetAccount } from 'soapbox/selectors'; import toast from 'soapbox/toast'; import ColumnForbidden from '../ui/components/column-forbidden'; @@ -62,14 +62,12 @@ const GroupBlockedMembers: React.FC = ({ params }) => { const intl = useIntl(); const dispatch = useAppDispatch(); - const id = params?.id || ''; + const id = params?.id; - const getGroup = useCallback(makeGetGroup(), []); - const group = useAppSelector(state => getGroup(state, id)); + const { group } = useGroup(id); const accountIds = useAppSelector((state) => state.user_lists.group_blocks.get(id)?.items); useEffect(() => { - if (!group) dispatch(fetchGroup(id)); dispatch(fetchGroupBlocks(id)); }, [id]); diff --git a/app/soapbox/features/group/group-membership-requests.tsx b/app/soapbox/features/group/group-membership-requests.tsx index 13f0925ac..fd33f3947 100644 --- a/app/soapbox/features/group/group-membership-requests.tsx +++ b/app/soapbox/features/group/group-membership-requests.tsx @@ -1,12 +1,12 @@ import React, { useCallback, useEffect } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; -import { authorizeGroupMembershipRequest, fetchGroup, fetchGroupMembershipRequests, rejectGroupMembershipRequest } from 'soapbox/actions/groups'; +import { authorizeGroupMembershipRequest, fetchGroupMembershipRequests, rejectGroupMembershipRequest } from 'soapbox/actions/groups'; import Account from 'soapbox/components/account'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Button, Column, HStack, Spinner } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import { makeGetAccount, makeGetGroup } from 'soapbox/selectors'; +import { useAppDispatch, useAppSelector, useGroup } from 'soapbox/hooks'; +import { makeGetAccount } from 'soapbox/selectors'; import toast from 'soapbox/toast'; import ColumnForbidden from '../ui/components/column-forbidden'; @@ -77,14 +77,12 @@ const GroupMembershipRequests: React.FC = ({ params }) const intl = useIntl(); const dispatch = useAppDispatch(); - const id = params?.id || ''; + const id = params?.id; - const getGroup = useCallback(makeGetGroup(), []); - const group = useAppSelector(state => getGroup(state, id)); + const { group } = useGroup(id); const accountIds = useAppSelector((state) => state.user_lists.membership_requests.get(id)?.items); useEffect(() => { - if (!group) dispatch(fetchGroup(id)); dispatch(fetchGroupMembershipRequests(id)); }, [id]); From 181bf23c345cf06a6dbc501c61974fc90ac229a3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 16 Mar 2023 13:15:00 -0500 Subject: [PATCH 5/7] Importer: use EntityStore enums --- app/soapbox/actions/importer/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/soapbox/actions/importer/index.ts b/app/soapbox/actions/importer/index.ts index 8d577cd49..ec0ec3121 100644 --- a/app/soapbox/actions/importer/index.ts +++ b/app/soapbox/actions/importer/index.ts @@ -1,4 +1,5 @@ import { importEntities } from 'soapbox/entity-store/actions'; +import { Entities } from 'soapbox/entity-store/entities'; import { Group, groupSchema } from 'soapbox/schemas'; import { filteredArray } from 'soapbox/schemas/utils'; @@ -23,10 +24,10 @@ const importAccounts = (accounts: APIEntity[]) => ({ type: ACCOUNTS_IMPORT, accounts }); const importGroup = (group: Group) => - importEntities([group], 'Group'); + importEntities([group], Entities.GROUPS); const importGroups = (groups: Group[]) => - importEntities(groups, 'Group'); + importEntities(groups, Entities.GROUPS); const importStatus = (status: APIEntity, idempotencyKey?: string) => (dispatch: AppDispatch, getState: () => RootState) => { From e6621a802b34131ee81481955a010f0442c5bce5 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 16 Mar 2023 13:18:36 -0500 Subject: [PATCH 6/7] Make popular and suggested groups share the Group store --- app/soapbox/entity-store/entities.ts | 2 -- app/soapbox/hooks/api/usePopularGroups.ts | 2 +- app/soapbox/hooks/api/useSuggestedGroups.ts | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/soapbox/entity-store/entities.ts b/app/soapbox/entity-store/entities.ts index 05d7b60d9..30220eed6 100644 --- a/app/soapbox/entity-store/entities.ts +++ b/app/soapbox/entity-store/entities.ts @@ -2,6 +2,4 @@ export enum Entities { GROUPS = 'Groups', GROUP_RELATIONSHIPS = 'GroupRelationships', GROUP_MEMBERSHIPS = 'GroupMemberships', - POPULAR_GROUPS = 'PopularGroups', - SUGGESTED_GROUPS = 'SuggestedGroups', } \ No newline at end of file diff --git a/app/soapbox/hooks/api/usePopularGroups.ts b/app/soapbox/hooks/api/usePopularGroups.ts index e856270a5..88ae48c9d 100644 --- a/app/soapbox/hooks/api/usePopularGroups.ts +++ b/app/soapbox/hooks/api/usePopularGroups.ts @@ -9,7 +9,7 @@ function usePopularGroups() { const features = useFeatures(); const { entities, ...result } = useEntities( - [Entities.POPULAR_GROUPS, ''], + [Entities.GROUPS, 'popular'], '/api/mock/groups', // '/api/v1/truth/trends/groups' { schema: groupSchema, diff --git a/app/soapbox/hooks/api/useSuggestedGroups.ts b/app/soapbox/hooks/api/useSuggestedGroups.ts index 54477c249..c1b85805c 100644 --- a/app/soapbox/hooks/api/useSuggestedGroups.ts +++ b/app/soapbox/hooks/api/useSuggestedGroups.ts @@ -9,7 +9,7 @@ function useSuggestedGroups() { const features = useFeatures(); const { entities, ...result } = useEntities( - [Entities.SUGGESTED_GROUPS, ''], + [Entities.GROUPS, 'suggested'], '/api/mock/groups', // '/api/v1/truth/suggestions/groups' { schema: groupSchema, From c0a22205f713408181383e796399cd913f01ef24 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 17 Mar 2023 16:14:23 -0500 Subject: [PATCH 7/7] Fix GroupActionButton test --- .../group/components/__tests__/group-action-button.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/features/group/components/__tests__/group-action-button.test.tsx b/app/soapbox/features/group/components/__tests__/group-action-button.test.tsx index 03f80fd9c..6809ea009 100644 --- a/app/soapbox/features/group/components/__tests__/group-action-button.test.tsx +++ b/app/soapbox/features/group/components/__tests__/group-action-button.test.tsx @@ -98,7 +98,7 @@ describe('', () => { relationship: buildGroupRelationship({ requested: false, member: true, - role: 'admin', + role: 'owner', }), }); });