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..ec0ec3121 100644
--- a/app/soapbox/actions/importer/index.ts
+++ b/app/soapbox/actions/importer/index.ts
@@ -1,3 +1,8 @@
+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';
+
import { getSettings } from '../settings';
import type { AppDispatch, RootState } from 'soapbox/store';
@@ -18,11 +23,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], Entities.GROUPS);
-const importGroups = (groups: APIEntity[]) =>
- ({ type: GROUPS_IMPORT, groups });
+const importGroups = (groups: Group[]) =>
+ importEntities(groups, Entities.GROUPS);
const importStatus = (status: APIEntity, idempotencyKey?: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
@@ -69,17 +74,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) =>
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/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',
}),
});
});
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..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]);
@@ -81,7 +79,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..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]);
@@ -96,7 +94,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..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 (
@@ -50,7 +43,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 +65,7 @@ const ManageGroup: React.FC = ({ params }) => {
return (
- {group.relationship.role === 'admin' && (
+ {group.relationship.role === 'owner' && (
@@ -83,7 +76,7 @@ const ManageGroup: React.FC = ({ params }) => {
- {group.relationship.role === 'admin' && (
+ {group.relationship.role === 'owner' && (
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,