Merge remote-tracking branch 'origin/develop' into group-requests
This commit is contained in:
commit
1949651b9a
21 changed files with 283 additions and 111 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useApi } from 'soapbox/hooks';
|
import { useApi } from 'soapbox/hooks';
|
||||||
|
|
||||||
import { useCreateEntity } from './useCreateEntity';
|
import { useCreateEntity } from './useCreateEntity';
|
||||||
|
@ -24,19 +26,24 @@ function useEntityActions<TEntity extends Entity = Entity, Params = any>(
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const { entityType, path } = parseEntitiesPath(expandedPath);
|
const { entityType, path } = parseEntitiesPath(expandedPath);
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
const deleteEntity = useDeleteEntity(entityType, (entityId) => {
|
const deleteEntity = useDeleteEntity(entityType, (entityId) => {
|
||||||
if (!endpoints.delete) return Promise.reject(endpoints);
|
if (!endpoints.delete) return Promise.reject(endpoints);
|
||||||
return api.delete(endpoints.delete.replace(':id', entityId));
|
return api.delete(endpoints.delete.replace(':id', entityId))
|
||||||
|
.finally(() => setIsLoading(false));
|
||||||
});
|
});
|
||||||
|
|
||||||
const createEntity = useCreateEntity(path, (params: Params) => {
|
const createEntity = useCreateEntity(path, (params: Params) => {
|
||||||
if (!endpoints.post) return Promise.reject(endpoints);
|
if (!endpoints.post) return Promise.reject(endpoints);
|
||||||
return api.post(endpoints.post, params);
|
return api.post(endpoints.post, params)
|
||||||
|
.finally(() => setIsLoading(false));
|
||||||
}, opts);
|
}, opts);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createEntity,
|
createEntity,
|
||||||
deleteEntity,
|
deleteEntity,
|
||||||
|
isLoading,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,49 +3,84 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import { Button } from 'soapbox/components/ui';
|
import { Button } from 'soapbox/components/ui';
|
||||||
|
import { deleteEntities } from 'soapbox/entity-store/actions';
|
||||||
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
import { useAppDispatch } from 'soapbox/hooks';
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
import { useCancelMembershipRequest, useJoinGroup, useLeaveGroup } from 'soapbox/queries/groups';
|
import { useCancelMembershipRequest, useJoinGroup, useLeaveGroup } from 'soapbox/hooks/api';
|
||||||
import { Group } from 'soapbox/types/entities';
|
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||||
|
import toast from 'soapbox/toast';
|
||||||
|
|
||||||
|
import type { Group } from 'soapbox/types/entities';
|
||||||
|
|
||||||
interface IGroupActionButton {
|
interface IGroupActionButton {
|
||||||
group: Group
|
group: Group
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
confirmationConfirm: { id: 'confirmations.leave_group.confirm', defaultMessage: 'Leave' },
|
||||||
confirmationHeading: { id: 'confirmations.leave_group.heading', defaultMessage: 'Leave group' },
|
confirmationHeading: { id: 'confirmations.leave_group.heading', defaultMessage: 'Leave group' },
|
||||||
confirmationMessage: { id: 'confirmations.leave_group.message', defaultMessage: 'You are about to leave the group. Do you want to continue?' },
|
confirmationMessage: { id: 'confirmations.leave_group.message', defaultMessage: 'You are about to leave the group. Do you want to continue?' },
|
||||||
confirmationConfirm: { id: 'confirmations.leave_group.confirm', defaultMessage: 'Leave' },
|
joinRequestSuccess: { id: 'group.join.request_success', defaultMessage: 'Requested to join the group' },
|
||||||
|
joinSuccess: { id: 'group.join.success', defaultMessage: 'Group joined successfully!' },
|
||||||
|
leaveSuccess: { id: 'group.leave.success', defaultMessage: 'Left the group' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const GroupActionButton = ({ group }: IGroupActionButton) => {
|
const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const joinGroup = useJoinGroup();
|
const joinGroup = useJoinGroup(group);
|
||||||
const leaveGroup = useLeaveGroup();
|
const leaveGroup = useLeaveGroup(group);
|
||||||
const cancelRequest = useCancelMembershipRequest();
|
const cancelRequest = useCancelMembershipRequest(group);
|
||||||
|
|
||||||
const isRequested = group.relationship?.requested;
|
const isRequested = group.relationship?.requested;
|
||||||
const isNonMember = !group.relationship?.member && !isRequested;
|
const isNonMember = !group.relationship?.member && !isRequested;
|
||||||
const isAdmin = group.relationship?.role === 'owner';
|
const isOwner = group.relationship?.role === GroupRoles.OWNER;
|
||||||
const isBlocked = group.relationship?.blocked_by;
|
const isBlocked = group.relationship?.blocked_by;
|
||||||
|
|
||||||
const onJoinGroup = () => joinGroup.mutate(group);
|
const onJoinGroup = () => joinGroup.mutate({}, {
|
||||||
|
onSuccess() {
|
||||||
|
toast.success(
|
||||||
|
group.locked
|
||||||
|
? intl.formatMessage(messages.joinRequestSuccess)
|
||||||
|
: intl.formatMessage(messages.joinSuccess),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const onLeaveGroup = () =>
|
const onLeaveGroup = () =>
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
heading: intl.formatMessage(messages.confirmationHeading),
|
heading: intl.formatMessage(messages.confirmationHeading),
|
||||||
message: intl.formatMessage(messages.confirmationMessage),
|
message: intl.formatMessage(messages.confirmationMessage),
|
||||||
confirm: intl.formatMessage(messages.confirmationConfirm),
|
confirm: intl.formatMessage(messages.confirmationConfirm),
|
||||||
onConfirm: () => leaveGroup.mutate(group),
|
onConfirm: () => leaveGroup.mutate({}, {
|
||||||
|
onSuccess() {
|
||||||
|
toast.success(intl.formatMessage(messages.leaveSuccess));
|
||||||
|
},
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const onCancelRequest = () => cancelRequest.mutate(group);
|
const onCancelRequest = () => cancelRequest.mutate({}, {
|
||||||
|
onSuccess() {
|
||||||
|
dispatch(deleteEntities([group.id], Entities.GROUP_RELATIONSHIPS));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (isBlocked) {
|
if (isBlocked) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isOwner) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
theme='secondary'
|
||||||
|
to={`/groups/${group.id}/manage`}
|
||||||
|
>
|
||||||
|
<FormattedMessage id='group.manage' defaultMessage='Manage Group' />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isNonMember) {
|
if (isNonMember) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
@ -72,17 +107,6 @@ const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAdmin) {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
theme='secondary'
|
|
||||||
to={`/groups/${group.id}/manage`}
|
|
||||||
>
|
|
||||||
<FormattedMessage id='group.manage' defaultMessage='Manage Group' />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
theme='secondary'
|
theme='secondary'
|
||||||
|
|
|
@ -2,11 +2,14 @@ import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { deleteGroup, editGroup } from 'soapbox/actions/groups';
|
import { editGroup } from 'soapbox/actions/groups';
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import List, { ListItem } from 'soapbox/components/list';
|
import List, { ListItem } from 'soapbox/components/list';
|
||||||
import { CardBody, Column, Spinner } from 'soapbox/components/ui';
|
import { CardBody, CardHeader, CardTitle, Column, Spinner, Text } from 'soapbox/components/ui';
|
||||||
import { useAppDispatch, useGroup } from 'soapbox/hooks';
|
import { useAppDispatch, useGroup, useGroupsPath } from 'soapbox/hooks';
|
||||||
|
import { useDeleteGroup } from 'soapbox/hooks/api';
|
||||||
|
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||||
|
import toast from 'soapbox/toast';
|
||||||
|
|
||||||
import ColumnForbidden from '../ui/components/column-forbidden';
|
import ColumnForbidden from '../ui/components/column-forbidden';
|
||||||
|
|
||||||
|
@ -16,11 +19,14 @@ const messages = defineMessages({
|
||||||
heading: { id: 'column.manage_group', defaultMessage: 'Manage group' },
|
heading: { id: 'column.manage_group', defaultMessage: 'Manage group' },
|
||||||
editGroup: { id: 'manage_group.edit_group', defaultMessage: 'Edit group' },
|
editGroup: { id: 'manage_group.edit_group', defaultMessage: 'Edit group' },
|
||||||
pendingRequests: { id: 'manage_group.pending_requests', defaultMessage: 'Pending requests' },
|
pendingRequests: { id: 'manage_group.pending_requests', defaultMessage: 'Pending requests' },
|
||||||
blockedMembers: { id: 'manage_group.blocked_members', defaultMessage: 'Blocked members' },
|
blockedMembers: { id: 'manage_group.blocked_members', defaultMessage: 'Banned members' },
|
||||||
deleteGroup: { id: 'manage_group.delete_group', defaultMessage: 'Delete group' },
|
deleteGroup: { id: 'manage_group.delete_group', defaultMessage: 'Delete group' },
|
||||||
deleteConfirm: { id: 'confirmations.delete_group.confirm', defaultMessage: 'Delete' },
|
deleteConfirm: { id: 'confirmations.delete_group.confirm', defaultMessage: 'Delete' },
|
||||||
deleteHeading: { id: 'confirmations.delete_group.heading', defaultMessage: 'Delete group' },
|
deleteHeading: { id: 'confirmations.delete_group.heading', defaultMessage: 'Delete group' },
|
||||||
deleteMessage: { id: 'confirmations.delete_group.message', defaultMessage: 'Are you sure you want to delete this group? This is a permanent action that cannot be undone.' },
|
deleteMessage: { id: 'confirmations.delete_group.message', defaultMessage: 'Are you sure you want to delete this group? This is a permanent action that cannot be undone.' },
|
||||||
|
members: { id: 'group.tabs.members', defaultMessage: 'Members' },
|
||||||
|
other: { id: 'settings.other', defaultMessage: 'Other options' },
|
||||||
|
deleteSuccess: { id: 'group.delete.success', defaultMessage: 'Group successfully deleted' },
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IManageGroup {
|
interface IManageGroup {
|
||||||
|
@ -32,9 +38,14 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const groupsPath = useGroupsPath();
|
||||||
|
|
||||||
const { group } = useGroup(id);
|
const { group } = useGroup(id);
|
||||||
|
|
||||||
|
const deleteGroup = useDeleteGroup();
|
||||||
|
|
||||||
|
const isOwner = group?.relationship?.role === GroupRoles.OWNER;
|
||||||
|
|
||||||
if (!group || !group.relationship) {
|
if (!group || !group.relationship) {
|
||||||
return (
|
return (
|
||||||
<Column label={intl.formatMessage(messages.heading)}>
|
<Column label={intl.formatMessage(messages.heading)}>
|
||||||
|
@ -56,7 +67,14 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||||
heading: intl.formatMessage(messages.deleteHeading),
|
heading: intl.formatMessage(messages.deleteHeading),
|
||||||
message: intl.formatMessage(messages.deleteMessage),
|
message: intl.formatMessage(messages.deleteMessage),
|
||||||
confirm: intl.formatMessage(messages.deleteConfirm),
|
confirm: intl.formatMessage(messages.deleteConfirm),
|
||||||
onConfirm: () => dispatch(deleteGroup(id)),
|
onConfirm: () => {
|
||||||
|
deleteGroup.mutate(group.id, {
|
||||||
|
onSuccess() {
|
||||||
|
toast.success(intl.formatMessage(messages.deleteSuccess));
|
||||||
|
history.push(groupsPath);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const navigateToPending = () => history.push(`/groups/${id}/manage/requests`);
|
const navigateToPending = () => history.push(`/groups/${id}/manage/requests`);
|
||||||
|
@ -65,21 +83,39 @@ const ManageGroup: React.FC<IManageGroup> = ({ params }) => {
|
||||||
return (
|
return (
|
||||||
<Column label={intl.formatMessage(messages.heading)} backHref={`/groups/${id}`}>
|
<Column label={intl.formatMessage(messages.heading)} backHref={`/groups/${id}`}>
|
||||||
<CardBody className='space-y-4'>
|
<CardBody className='space-y-4'>
|
||||||
{group.relationship.role === 'owner' && (
|
{isOwner && (
|
||||||
|
<>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle title={intl.formatMessage(messages.editGroup)} />
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
<List>
|
<List>
|
||||||
<ListItem label={intl.formatMessage(messages.editGroup)} onClick={onEditGroup}>
|
<ListItem label={intl.formatMessage(messages.editGroup)} onClick={onEditGroup}>
|
||||||
<span dangerouslySetInnerHTML={{ __html: group.display_name_html }} />
|
<span dangerouslySetInnerHTML={{ __html: group.display_name_html }} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle title={intl.formatMessage(messages.members)} />
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
<List>
|
<List>
|
||||||
<ListItem label={intl.formatMessage(messages.pendingRequests)} onClick={navigateToPending} />
|
<ListItem label={intl.formatMessage(messages.pendingRequests)} onClick={navigateToPending} />
|
||||||
<ListItem label={intl.formatMessage(messages.blockedMembers)} onClick={navigateToBlocks} />
|
<ListItem label={intl.formatMessage(messages.blockedMembers)} onClick={navigateToBlocks} />
|
||||||
</List>
|
</List>
|
||||||
{group.relationship.role === 'owner' && (
|
|
||||||
|
{isOwner && (
|
||||||
|
<>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle title={intl.formatMessage(messages.other)} />
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
<List>
|
<List>
|
||||||
<ListItem label={intl.formatMessage(messages.deleteGroup)} onClick={onDeleteGroup} />
|
<ListItem label={<Text theme='danger'>{intl.formatMessage(messages.deleteGroup)}</Text>} onClick={onDeleteGroup} />
|
||||||
</List>
|
</List>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import GroupAvatar from 'soapbox/components/groups/group-avatar';
|
||||||
import { Button, HStack, Stack, Text } from 'soapbox/components/ui';
|
import { Button, HStack, Stack, Text } from 'soapbox/components/ui';
|
||||||
import GroupMemberCount from 'soapbox/features/group/components/group-member-count';
|
import GroupMemberCount from 'soapbox/features/group/components/group-member-count';
|
||||||
import GroupPrivacy from 'soapbox/features/group/components/group-privacy';
|
import GroupPrivacy from 'soapbox/features/group/components/group-privacy';
|
||||||
import { useJoinGroup } from 'soapbox/queries/groups';
|
import { useJoinGroup } from 'soapbox/hooks/api';
|
||||||
import { Group as GroupEntity } from 'soapbox/types/entities';
|
import { Group as GroupEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
interface IGroup {
|
interface IGroup {
|
||||||
|
@ -17,7 +17,7 @@ interface IGroup {
|
||||||
const GroupGridItem = forwardRef((props: IGroup, ref: React.ForwardedRef<HTMLDivElement>) => {
|
const GroupGridItem = forwardRef((props: IGroup, ref: React.ForwardedRef<HTMLDivElement>) => {
|
||||||
const { group, width = 'auto' } = props;
|
const { group, width = 'auto' } = props;
|
||||||
|
|
||||||
const joinGroup = useJoinGroup();
|
const joinGroup = useJoinGroup(group);
|
||||||
|
|
||||||
const onJoinGroup = () => joinGroup.mutate(group);
|
const onJoinGroup = () => joinGroup.mutate(group);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import GroupAvatar from 'soapbox/components/groups/group-avatar';
|
import GroupAvatar from 'soapbox/components/groups/group-avatar';
|
||||||
import { Button, HStack, Icon, Stack, Text } from 'soapbox/components/ui';
|
import { Button, HStack, Icon, Stack, Text } from 'soapbox/components/ui';
|
||||||
import { useJoinGroup } from 'soapbox/queries/groups';
|
import { useJoinGroup } from 'soapbox/hooks/api';
|
||||||
import { Group as GroupEntity } from 'soapbox/types/entities';
|
import { Group as GroupEntity } from 'soapbox/types/entities';
|
||||||
import { shortNumberFormat } from 'soapbox/utils/numbers';
|
import { shortNumberFormat } from 'soapbox/utils/numbers';
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ interface IGroup {
|
||||||
const GroupListItem = (props: IGroup) => {
|
const GroupListItem = (props: IGroup) => {
|
||||||
const { group, withJoinAction = true } = props;
|
const { group, withJoinAction = true } = props;
|
||||||
|
|
||||||
const joinGroup = useJoinGroup();
|
const joinGroup = useJoinGroup(group);
|
||||||
|
|
||||||
const onJoinGroup = () => joinGroup.mutate(group);
|
const onJoinGroup = () => joinGroup.mutate(group);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { fetchMfa } from 'soapbox/actions/mfa';
|
import { fetchMfa } from 'soapbox/actions/mfa';
|
||||||
import List, { ListItem } from 'soapbox/components/list';
|
import List, { ListItem } from 'soapbox/components/list';
|
||||||
import { Card, CardBody, CardHeader, CardTitle, Column } from 'soapbox/components/ui';
|
import { Card, CardBody, CardHeader, CardTitle, Column, Text } from 'soapbox/components/ui';
|
||||||
import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks';
|
import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks';
|
||||||
|
|
||||||
import Preferences from '../preferences';
|
import Preferences from '../preferences';
|
||||||
|
@ -155,7 +155,7 @@ const Settings = () => {
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{features.security && (
|
{features.security && (
|
||||||
<ListItem label={intl.formatMessage(messages.deleteAccount)} onClick={navigateToDeleteAccount} />
|
<ListItem label={<Text theme='danger'>{intl.formatMessage(messages.deleteAccount)}</Text>} onClick={navigateToDeleteAccount} />
|
||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|
|
@ -114,7 +114,11 @@ export const getDescendantsIds = createSelector([
|
||||||
});
|
});
|
||||||
|
|
||||||
type DisplayMedia = 'default' | 'hide_all' | 'show_all';
|
type DisplayMedia = 'default' | 'hide_all' | 'show_all';
|
||||||
type RouteParams = { statusId: string };
|
|
||||||
|
type RouteParams = {
|
||||||
|
statusId: string
|
||||||
|
groupId?: string
|
||||||
|
};
|
||||||
|
|
||||||
interface IThread {
|
interface IThread {
|
||||||
params: RouteParams
|
params: RouteParams
|
||||||
|
@ -515,6 +519,10 @@ const Thread: React.FC<IThread> = (props) => {
|
||||||
children.push(...renderChildren(descendantsIds).toArray());
|
children.push(...renderChildren(descendantsIds).toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status.group && typeof status.group === 'object' && !props.params.groupId) {
|
||||||
|
return <Redirect to={`/groups/${status.group.id}/posts/${props.params.statusId}`} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column label={intl.formatMessage(titleMessage, { username })} transparent>
|
<Column label={intl.formatMessage(titleMessage, { username })} transparent>
|
||||||
<PullToRefresh onRefresh={handleRefresh}>
|
<PullToRefresh onRefresh={handleRefresh}>
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { Widget } from 'soapbox/components/ui';
|
||||||
|
import GroupListItem from 'soapbox/features/groups/components/discover/group-list-item';
|
||||||
|
import PlaceholderGroupSearch from 'soapbox/features/placeholder/components/placeholder-group-search';
|
||||||
|
import { useGroups } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
const MyGroupsPanel = () => {
|
||||||
|
const { groups, isFetching, isFetched, isError } = useGroups();
|
||||||
|
const isEmpty = (isFetched && groups.length === 0) || isError;
|
||||||
|
|
||||||
|
if (isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Widget
|
||||||
|
title='My Groups'
|
||||||
|
>
|
||||||
|
{isFetching ? (
|
||||||
|
new Array(3).fill(0).map((_, idx) => (
|
||||||
|
<PlaceholderGroupSearch key={idx} />
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
groups.slice(0, 3).map((group) => (
|
||||||
|
<GroupListItem group={group} withJoinAction={false} key={group.id} />
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MyGroupsPanel;
|
|
@ -299,6 +299,7 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
|
||||||
{features.groups && <WrappedRoute path='/groups/:id/manage' exact page={DefaultPage} component={ManageGroup} content={children} />}
|
{features.groups && <WrappedRoute path='/groups/:id/manage' exact page={DefaultPage} component={ManageGroup} content={children} />}
|
||||||
{features.groups && <WrappedRoute path='/groups/:id/manage/blocks' exact page={DefaultPage} component={GroupBlockedMembers} content={children} />}
|
{features.groups && <WrappedRoute path='/groups/:id/manage/blocks' exact page={DefaultPage} component={GroupBlockedMembers} content={children} />}
|
||||||
{features.groups && <WrappedRoute path='/groups/:id/manage/requests' exact page={DefaultPage} component={GroupMembershipRequests} content={children} />}
|
{features.groups && <WrappedRoute path='/groups/:id/manage/requests' exact page={DefaultPage} component={GroupMembershipRequests} content={children} />}
|
||||||
|
{features.groups && <WrappedRoute path='/groups/:groupId/posts/:statusId' exact page={StatusPage} component={Status} content={children} />}
|
||||||
|
|
||||||
<WrappedRoute path='/statuses/new' page={DefaultPage} component={NewStatus} content={children} exact />
|
<WrappedRoute path='/statuses/new' page={DefaultPage} component={NewStatus} content={children} exact />
|
||||||
<WrappedRoute path='/statuses/:statusId' exact page={StatusPage} component={Status} content={children} />
|
<WrappedRoute path='/statuses/:statusId' exact page={StatusPage} component={Status} content={children} />
|
||||||
|
|
|
@ -590,6 +590,10 @@ export function NewGroupPanel() {
|
||||||
return import(/* webpackChunkName: "features/groups" */'../components/panels/new-group-panel');
|
return import(/* webpackChunkName: "features/groups" */'../components/panels/new-group-panel');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function MyGroupsPanel() {
|
||||||
|
return import(/* webpackChunkName: "features/groups" */'../components/panels/my-groups-panel');
|
||||||
|
}
|
||||||
|
|
||||||
export function SuggestedGroupsPanel() {
|
export function SuggestedGroupsPanel() {
|
||||||
return import(/* webpackChunkName: "features/groups" */'../components/panels/suggested-groups-panel');
|
return import(/* webpackChunkName: "features/groups" */'../components/panels/suggested-groups-panel');
|
||||||
}
|
}
|
||||||
|
|
21
app/soapbox/hooks/api/groups/useCancelMembershipRequest.ts
Normal file
21
app/soapbox/hooks/api/groups/useCancelMembershipRequest.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
|
import { useEntityActions } from 'soapbox/entity-store/hooks';
|
||||||
|
import { useOwnAccount } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
import type { Group, GroupRelationship } from 'soapbox/schemas';
|
||||||
|
|
||||||
|
function useCancelMembershipRequest(group: Group) {
|
||||||
|
const me = useOwnAccount();
|
||||||
|
|
||||||
|
const { createEntity, isLoading } = useEntityActions<GroupRelationship>(
|
||||||
|
[Entities.GROUP_RELATIONSHIPS, group.id],
|
||||||
|
{ post: `/api/v1/groups/${group.id}/membership_requests/${me?.id}/reject` },
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mutate: createEntity,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useCancelMembershipRequest };
|
18
app/soapbox/hooks/api/groups/useDeleteGroup.ts
Normal file
18
app/soapbox/hooks/api/groups/useDeleteGroup.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
|
import { useEntityActions } from 'soapbox/entity-store/hooks';
|
||||||
|
|
||||||
|
import type { Group } from 'soapbox/schemas';
|
||||||
|
|
||||||
|
function useDeleteGroup() {
|
||||||
|
const { deleteEntity, isLoading } = useEntityActions<Group>(
|
||||||
|
[Entities.GROUPS],
|
||||||
|
{ delete: '/api/v1/groups/:id' },
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mutate: deleteEntity,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useDeleteGroup };
|
20
app/soapbox/hooks/api/groups/useJoinGroup.ts
Normal file
20
app/soapbox/hooks/api/groups/useJoinGroup.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
|
import { useEntityActions } from 'soapbox/entity-store/hooks';
|
||||||
|
import { groupRelationshipSchema } from 'soapbox/schemas';
|
||||||
|
|
||||||
|
import type { Group, GroupRelationship } from 'soapbox/schemas';
|
||||||
|
|
||||||
|
function useJoinGroup(group: Group) {
|
||||||
|
const { createEntity, isLoading } = useEntityActions<GroupRelationship>(
|
||||||
|
[Entities.GROUP_RELATIONSHIPS, group.id],
|
||||||
|
{ post: `/api/v1/groups/${group.id}/join` },
|
||||||
|
{ schema: groupRelationshipSchema },
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mutate: createEntity,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useJoinGroup };
|
18
app/soapbox/hooks/api/groups/useLeaveGroup.ts
Normal file
18
app/soapbox/hooks/api/groups/useLeaveGroup.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
|
import { useEntityActions } from 'soapbox/entity-store/hooks';
|
||||||
|
import { Group, GroupRelationship, groupRelationshipSchema } from 'soapbox/schemas';
|
||||||
|
|
||||||
|
function useLeaveGroup(group: Group) {
|
||||||
|
const { createEntity, isLoading } = useEntityActions<GroupRelationship>(
|
||||||
|
[Entities.GROUP_RELATIONSHIPS, group.id],
|
||||||
|
{ post: `/api/v1/groups/${group.id}/leave` },
|
||||||
|
{ schema: groupRelationshipSchema },
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mutate: createEntity,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useLeaveGroup };
|
|
@ -2,5 +2,9 @@
|
||||||
* Groups
|
* Groups
|
||||||
*/
|
*/
|
||||||
export { useBlockGroupMember } from './groups/useBlockGroupMember';
|
export { useBlockGroupMember } from './groups/useBlockGroupMember';
|
||||||
|
export { useCancelMembershipRequest } from './groups/useCancelMembershipRequest';
|
||||||
|
export { useDeleteGroup } from './groups/useDeleteGroup';
|
||||||
export { useDemoteGroupMember } from './groups/useDemoteGroupMember';
|
export { useDemoteGroupMember } from './groups/useDemoteGroupMember';
|
||||||
|
export { useJoinGroup } from './groups/useJoinGroup';
|
||||||
|
export { useLeaveGroup } from './groups/useLeaveGroup';
|
||||||
export { usePromoteGroupMember } from './groups/usePromoteGroupMember';
|
export { usePromoteGroupMember } from './groups/usePromoteGroupMember';
|
|
@ -5,11 +5,15 @@ import { useEntities, useEntity } from 'soapbox/entity-store/hooks';
|
||||||
import { groupSchema, Group } from 'soapbox/schemas/group';
|
import { groupSchema, Group } from 'soapbox/schemas/group';
|
||||||
import { groupRelationshipSchema, GroupRelationship } from 'soapbox/schemas/group-relationship';
|
import { groupRelationshipSchema, GroupRelationship } from 'soapbox/schemas/group-relationship';
|
||||||
|
|
||||||
|
import { useFeatures } from './useFeatures';
|
||||||
|
|
||||||
function useGroups() {
|
function useGroups() {
|
||||||
|
const features = useFeatures();
|
||||||
|
|
||||||
const { entities, ...result } = useEntities<Group>(
|
const { entities, ...result } = useEntities<Group>(
|
||||||
[Entities.GROUPS],
|
[Entities.GROUPS],
|
||||||
'/api/v1/groups',
|
'/api/v1/groups',
|
||||||
{ schema: groupSchema },
|
{ enabled: features.groups, schema: groupSchema },
|
||||||
);
|
);
|
||||||
const { relationships } = useGroupRelationships(entities.map(entity => entity.id));
|
const { relationships } = useGroupRelationships(entities.map(entity => entity.id));
|
||||||
|
|
||||||
|
|
|
@ -767,6 +767,7 @@
|
||||||
"gdpr.title": "{siteTitle} uses cookies",
|
"gdpr.title": "{siteTitle} uses cookies",
|
||||||
"getting_started.open_source_notice": "{code_name} is open source software. You can contribute or report issues at {code_link} (v{code_version}).",
|
"getting_started.open_source_notice": "{code_name} is open source software. You can contribute or report issues at {code_link} (v{code_version}).",
|
||||||
"group.cancel_request": "Cancel Request",
|
"group.cancel_request": "Cancel Request",
|
||||||
|
"group.delete.success": "Group successfully deleted",
|
||||||
"group.demote.user.success": "@{name} is now a member",
|
"group.demote.user.success": "@{name} is now a member",
|
||||||
"group.group_mod_authorize.fail": "Failed to approve @{name}",
|
"group.group_mod_authorize.fail": "Failed to approve @{name}",
|
||||||
"group.group_mod_block": "Ban from group",
|
"group.group_mod_block": "Ban from group",
|
||||||
|
@ -926,7 +927,7 @@
|
||||||
"login_external.errors.instance_fail": "The instance returned an error.",
|
"login_external.errors.instance_fail": "The instance returned an error.",
|
||||||
"login_external.errors.network_fail": "Connection failed. Is a browser extension blocking it?",
|
"login_external.errors.network_fail": "Connection failed. Is a browser extension blocking it?",
|
||||||
"login_form.header": "Sign In",
|
"login_form.header": "Sign In",
|
||||||
"manage_group.blocked_members": "Blocked members",
|
"manage_group.blocked_members": "Banned members",
|
||||||
"manage_group.confirmation.copy": "Copy link",
|
"manage_group.confirmation.copy": "Copy link",
|
||||||
"manage_group.confirmation.info_1": "As the owner of this group, you can assign staff, delete posts and much more.",
|
"manage_group.confirmation.info_1": "As the owner of this group, you can assign staff, delete posts and much more.",
|
||||||
"manage_group.confirmation.info_2": "Post the group's first post and get the conversation started.",
|
"manage_group.confirmation.info_2": "Post the group's first post and get the conversation started.",
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
CtaBanner,
|
CtaBanner,
|
||||||
GroupMediaPanel,
|
GroupMediaPanel,
|
||||||
SignUpPanel,
|
SignUpPanel,
|
||||||
|
SuggestedGroupsPanel,
|
||||||
} from 'soapbox/features/ui/util/async-components';
|
} from 'soapbox/features/ui/util/async-components';
|
||||||
import { useGroup, useOwnAccount } from 'soapbox/hooks';
|
import { useGroup, useOwnAccount } from 'soapbox/hooks';
|
||||||
import { useGroupMembershipRequests } from 'soapbox/hooks/api/groups/useGroupMembershipRequests';
|
import { useGroupMembershipRequests } from 'soapbox/hooks/api/groups/useGroupMembershipRequests';
|
||||||
|
@ -124,6 +125,9 @@ const GroupPage: React.FC<IGroupPage> = ({ params, children }) => {
|
||||||
<BundleContainer fetchComponent={GroupMediaPanel}>
|
<BundleContainer fetchComponent={GroupMediaPanel}>
|
||||||
{Component => <Component group={group} />}
|
{Component => <Component group={group} />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
|
<BundleContainer fetchComponent={SuggestedGroupsPanel}>
|
||||||
|
{Component => <Component />}
|
||||||
|
</BundleContainer>
|
||||||
<LinkFooter key='link-footer' />
|
<LinkFooter key='link-footer' />
|
||||||
</Layout.Aside>
|
</Layout.Aside>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Route, Routes } from 'react-router-dom-v5-compat';
|
||||||
|
|
||||||
import { Column, Layout } from 'soapbox/components/ui';
|
import { Column, Layout } from 'soapbox/components/ui';
|
||||||
import LinkFooter from 'soapbox/features/ui/components/link-footer';
|
import LinkFooter from 'soapbox/features/ui/components/link-footer';
|
||||||
import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
|
import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
|
||||||
import { NewGroupPanel } from 'soapbox/features/ui/util/async-components';
|
import { MyGroupsPanel, NewGroupPanel, SuggestedGroupsPanel } from 'soapbox/features/ui/util/async-components';
|
||||||
|
|
||||||
interface IGroupsPage {
|
interface IGroupsPage {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
|
@ -22,10 +23,28 @@ const GroupsPage: React.FC<IGroupsPage> = ({ children }) => (
|
||||||
|
|
||||||
<Layout.Aside>
|
<Layout.Aside>
|
||||||
<BundleContainer fetchComponent={NewGroupPanel}>
|
<BundleContainer fetchComponent={NewGroupPanel}>
|
||||||
{Component => <Component key='new-group-panel' />}
|
{Component => <Component />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path='/groups'
|
||||||
|
element={(
|
||||||
|
<BundleContainer fetchComponent={SuggestedGroupsPanel}>
|
||||||
|
{Component => <Component />}
|
||||||
|
</BundleContainer>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path='/groups/discover'
|
||||||
|
element={(
|
||||||
|
<BundleContainer fetchComponent={MyGroupsPanel}>
|
||||||
|
{Component => <Component />}
|
||||||
|
</BundleContainer>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
|
||||||
<LinkFooter key='link-footer' />
|
<LinkFooter />
|
||||||
</Layout.Aside>
|
</Layout.Aside>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
|
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
|
||||||
import { AxiosRequestConfig } from 'axios';
|
import { AxiosRequestConfig } from 'axios';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import { getNextLink } from 'soapbox/api';
|
import { getNextLink } from 'soapbox/api';
|
||||||
import { useApi, useFeatures, useOwnAccount } from 'soapbox/hooks';
|
import { useApi, useFeatures, useOwnAccount } from 'soapbox/hooks';
|
||||||
import { normalizeGroup, normalizeGroupRelationship } from 'soapbox/normalizers';
|
import { normalizeGroup, normalizeGroupRelationship } from 'soapbox/normalizers';
|
||||||
import toast from 'soapbox/toast';
|
|
||||||
import { Group, GroupRelationship } from 'soapbox/types/entities';
|
import { Group, GroupRelationship } from 'soapbox/types/entities';
|
||||||
import { flattenPages, PaginatedResult } from 'soapbox/utils/queries';
|
import { flattenPages, PaginatedResult } from 'soapbox/utils/queries';
|
||||||
|
|
||||||
import { queryClient } from './client';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
joinSuccess: { id: 'group.join.success', defaultMessage: 'Group joined successfully!' },
|
|
||||||
joinRequestSuccess: { id: 'group.join.request_success', defaultMessage: 'Requested to join the group' },
|
|
||||||
leaveSuccess: { id: 'group.leave.success', defaultMessage: 'Left the group' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const GroupKeys = {
|
const GroupKeys = {
|
||||||
group: (id: string) => ['groups', 'group', id] as const,
|
group: (id: string) => ['groups', 'group', id] as const,
|
||||||
myGroups: (userId: string) => ['groups', userId] as const,
|
myGroups: (userId: string) => ['groups', userId] as const,
|
||||||
|
@ -168,50 +158,8 @@ const useGroup = (id: string) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const useJoinGroup = () => {
|
|
||||||
const api = useApi();
|
|
||||||
const intl = useIntl();
|
|
||||||
|
|
||||||
return useMutation((group: Group) => api.post<GroupRelationship>(`/api/v1/groups/${group.id}/join`), {
|
|
||||||
onSuccess(_response, group) {
|
|
||||||
queryClient.invalidateQueries(['groups']);
|
|
||||||
toast.success(
|
|
||||||
group.locked
|
|
||||||
? intl.formatMessage(messages.joinRequestSuccess)
|
|
||||||
: intl.formatMessage(messages.joinSuccess),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const useLeaveGroup = () => {
|
|
||||||
const api = useApi();
|
|
||||||
const intl = useIntl();
|
|
||||||
|
|
||||||
return useMutation((group: Group) => api.post<GroupRelationship>(`/api/v1/groups/${group.id}/leave`), {
|
|
||||||
onSuccess() {
|
|
||||||
queryClient.invalidateQueries({ queryKey: ['groups'] });
|
|
||||||
toast.success(intl.formatMessage(messages.leaveSuccess));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const useCancelMembershipRequest = () => {
|
|
||||||
const api = useApi();
|
|
||||||
const me = useOwnAccount();
|
|
||||||
|
|
||||||
return useMutation((group: Group) => api.post(`/api/v1/groups/${group.id}/membership_requests/${me?.id}/reject`), {
|
|
||||||
onSuccess() {
|
|
||||||
queryClient.invalidateQueries({ queryKey: ['groups'] });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useCancelMembershipRequest,
|
|
||||||
useGroup,
|
useGroup,
|
||||||
useGroups,
|
useGroups,
|
||||||
useJoinGroup,
|
|
||||||
useLeaveGroup,
|
|
||||||
usePendingGroups,
|
usePendingGroups,
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
import { getSettings } from 'soapbox/actions/settings';
|
||||||
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
import { getDomain } from 'soapbox/utils/accounts';
|
import { getDomain } from 'soapbox/utils/accounts';
|
||||||
import { validId } from 'soapbox/utils/auth';
|
import { validId } from 'soapbox/utils/auth';
|
||||||
import ConfigDB from 'soapbox/utils/config-db';
|
import ConfigDB from 'soapbox/utils/config-db';
|
||||||
|
@ -14,9 +15,10 @@ import { getFeatures } from 'soapbox/utils/features';
|
||||||
import { shouldFilter } from 'soapbox/utils/timelines';
|
import { shouldFilter } from 'soapbox/utils/timelines';
|
||||||
|
|
||||||
import type { ContextType } from 'soapbox/normalizers/filter';
|
import type { ContextType } from 'soapbox/normalizers/filter';
|
||||||
|
import type { ReducerAccount } from 'soapbox/reducers/accounts';
|
||||||
import type { ReducerChat } from 'soapbox/reducers/chats';
|
import type { ReducerChat } from 'soapbox/reducers/chats';
|
||||||
import type { RootState } from 'soapbox/store';
|
import type { RootState } from 'soapbox/store';
|
||||||
import type { Filter as FilterEntity, Notification } from 'soapbox/types/entities';
|
import type { Filter as FilterEntity, Notification, Status, Group } from 'soapbox/types/entities';
|
||||||
|
|
||||||
const normalizeId = (id: any): string => typeof id === 'string' ? id : '';
|
const normalizeId = (id: any): string => typeof id === 'string' ? id : '';
|
||||||
|
|
||||||
|
@ -180,11 +182,11 @@ type APIStatus = { id: string, username?: string };
|
||||||
export const makeGetStatus = () => {
|
export const makeGetStatus = () => {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
[
|
[
|
||||||
(state: RootState, { id }: APIStatus) => state.statuses.get(id),
|
(state: RootState, { id }: APIStatus) => state.statuses.get(id) as Status | undefined,
|
||||||
(state: RootState, { id }: APIStatus) => state.statuses.get(state.statuses.get(id)?.reblog || ''),
|
(state: RootState, { id }: APIStatus) => state.statuses.get(state.statuses.get(id)?.reblog || '') as Status | undefined,
|
||||||
(state: RootState, { id }: APIStatus) => state.accounts.get(state.statuses.get(id)?.account || ''),
|
(state: RootState, { id }: APIStatus) => state.accounts.get(state.statuses.get(id)?.account || '') as ReducerAccount | undefined,
|
||||||
(state: RootState, { id }: APIStatus) => state.accounts.get(state.statuses.get(state.statuses.get(id)?.reblog || '')?.account || ''),
|
(state: RootState, { id }: APIStatus) => state.accounts.get(state.statuses.get(state.statuses.get(id)?.reblog || '')?.account || '') as ReducerAccount | undefined,
|
||||||
(state: RootState, { id }: APIStatus) => state.groups.items.get(state.statuses.get(id)?.group || ''),
|
(state: RootState, { id }: APIStatus) => state.entities[Entities.GROUPS]?.store[state.statuses.get(id)?.group || ''] as Group | undefined,
|
||||||
(_state: RootState, { username }: APIStatus) => username,
|
(_state: RootState, { username }: APIStatus) => username,
|
||||||
getFilters,
|
getFilters,
|
||||||
(state: RootState) => state.me,
|
(state: RootState) => state.me,
|
||||||
|
@ -207,7 +209,7 @@ export const makeGetStatus = () => {
|
||||||
statusReblog = undefined;
|
statusReblog = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return statusBase.withMutations(map => {
|
return statusBase.withMutations((map: Status) => {
|
||||||
map.set('reblog', statusReblog || null);
|
map.set('reblog', statusReblog || null);
|
||||||
// @ts-ignore :(
|
// @ts-ignore :(
|
||||||
map.set('account', accountBase || null);
|
map.set('account', accountBase || null);
|
||||||
|
|
Loading…
Reference in a new issue