Groups: Snackbar messages, timelines handling
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
d299512694
commit
bed8619cf0
|
@ -1,18 +1,18 @@
|
|||
import { GroupRole } from 'soapbox/reducers/group-memberships';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import api, { getLinks } from '../api';
|
||||
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importFetchedGroups, importFetchedAccounts } from './importer';
|
||||
import { closeModal, openModal } from './modals';
|
||||
import snackbar from './snackbar';
|
||||
import { deleteFromTimelines } from './timelines';
|
||||
|
||||
import type { AxiosError } from 'axios';
|
||||
import type { GroupRole } from 'soapbox/reducers/group-memberships';
|
||||
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||
import type { APIEntity, Group } from 'soapbox/types/entities';
|
||||
|
||||
type GroupMedia = 'header' | 'avatar';
|
||||
|
||||
const GROUP_EDITOR_SET = 'GROUP_EDITOR_SET';
|
||||
|
||||
const GROUP_CREATE_REQUEST = 'GROUP_CREATE_REQUEST';
|
||||
|
@ -110,6 +110,15 @@ const GROUP_EDITOR_MEDIA_CHANGE = 'GROUP_EDITOR_MEDIA_CHANGE';
|
|||
|
||||
const GROUP_EDITOR_RESET = 'GROUP_EDITOR_RESET';
|
||||
|
||||
const messages = defineMessages({
|
||||
success: { id: 'manage_group.submit_success', defaultMessage: 'The group was created' },
|
||||
editSuccess: { id: 'manage_group.edit_success', defaultMessage: 'The group was edited' },
|
||||
joinSuccess: { id: 'group.join.success', defaultMessage: 'Joined the group' },
|
||||
joinRequestSuccess: { id: 'group.join.request_success', defaultMessage: 'Requested to join the group' },
|
||||
leaveSuccess: { id: 'group.leave.success', defaultMessage: 'Left the group' },
|
||||
view: { id: 'snackbar.view', defaultMessage: 'View' },
|
||||
});
|
||||
|
||||
const editGroup = (group: Group) => (dispatch: AppDispatch) => {
|
||||
dispatch({
|
||||
type: GROUP_EDITOR_SET,
|
||||
|
@ -130,6 +139,7 @@ const createGroup = (params: Record<string, any>, shouldReset?: boolean) =>
|
|||
.then(({ data }) => {
|
||||
dispatch(importFetchedGroups([data]));
|
||||
dispatch(createGroupSuccess(data));
|
||||
dispatch(snackbar.success(messages.success, messages.view, `/groups/${data.id}`));
|
||||
|
||||
if (shouldReset) {
|
||||
dispatch(resetGroupEditor());
|
||||
|
@ -160,6 +170,7 @@ const updateGroup = (id: string, params: Record<string, any>, shouldReset?: bool
|
|||
.then(({ data }) => {
|
||||
dispatch(importFetchedGroups([data]));
|
||||
dispatch(updateGroupSuccess(data));
|
||||
dispatch(snackbar.success(messages.editSuccess));
|
||||
|
||||
if (shouldReset) {
|
||||
dispatch(resetGroupEditor());
|
||||
|
@ -305,6 +316,7 @@ const joinGroup = (id: string) =>
|
|||
|
||||
api(getState).post(`/api/v1/groups/${id}/join`).then(response => {
|
||||
dispatch(joinGroupSuccess(response.data));
|
||||
dispatch(snackbar.success(locked ? messages.joinRequestSuccess : messages.joinSuccess));
|
||||
}).catch(error => {
|
||||
dispatch(joinGroupFail(error, locked));
|
||||
});
|
||||
|
@ -316,6 +328,7 @@ const leaveGroup = (id: string) =>
|
|||
|
||||
api(getState).post(`/api/v1/groups/${id}/leave`).then(response => {
|
||||
dispatch(leaveGroupSuccess(response.data));
|
||||
dispatch(snackbar.success(messages.leaveSuccess));
|
||||
}).catch(error => {
|
||||
dispatch(leaveGroupFail(error));
|
||||
});
|
||||
|
@ -826,7 +839,7 @@ const changeGroupEditorPrivacy = (value: boolean) => ({
|
|||
value,
|
||||
});
|
||||
|
||||
const changeGroupEditorMedia = (mediaType: GroupMedia, file: File) => ({
|
||||
const changeGroupEditorMedia = (mediaType: 'header' | 'avatar', file: File) => ({
|
||||
type: GROUP_EDITOR_MEDIA_CHANGE,
|
||||
mediaType,
|
||||
value: file,
|
||||
|
|
|
@ -80,11 +80,12 @@ const StatusActionButton = React.forwardRef<HTMLButtonElement, IStatusActionButt
|
|||
type='button'
|
||||
className={classNames(
|
||||
'flex items-center p-1 rounded-full',
|
||||
'text-gray-600 hover:text-gray-600 dark:hover:text-white',
|
||||
'text-gray-600',
|
||||
'bg-white dark:bg-transparent',
|
||||
'focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 dark:ring-offset-0',
|
||||
{
|
||||
'text-black dark:text-white': active && emoji,
|
||||
'hover:text-gray-600 dark:hover:text-white': !filteredProps.disabled,
|
||||
'text-accent-300 hover:text-accent-300 dark:hover:text-accent-300': active && !emoji && color === COLORS.accent,
|
||||
'text-success-600 hover:text-success-600 dark:hover:text-success-600': active && !emoji && color === COLORS.success,
|
||||
'space-x-1': !text,
|
||||
|
|
|
@ -46,6 +46,8 @@ interface IStatusList extends Omit<IScrollableList, 'onLoadMore' | 'children'> {
|
|||
divideType?: 'space' | 'border',
|
||||
/** Whether to display ads. */
|
||||
showAds?: boolean,
|
||||
/** Whether to show group information. */
|
||||
showGroup?: boolean,
|
||||
}
|
||||
|
||||
/** Feed of statuses, built atop ScrollableList. */
|
||||
|
@ -59,6 +61,7 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
isLoading,
|
||||
isPartial,
|
||||
showAds = false,
|
||||
showGroup = true,
|
||||
...other
|
||||
}) => {
|
||||
const { data: ads } = useAds();
|
||||
|
@ -135,6 +138,7 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
onMoveUp={handleMoveUp}
|
||||
onMoveDown={handleMoveDown}
|
||||
contextType={timelineId}
|
||||
showGroup={showGroup}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -167,6 +171,7 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
onMoveUp={handleMoveUp}
|
||||
onMoveDown={handleMoveDown}
|
||||
contextType={timelineId}
|
||||
showGroup={showGroup}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import classNames from 'clsx';
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||
import { NavLink, useHistory } from 'react-router-dom';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
|
||||
import { mentionCompose, replyCompose } from 'soapbox/actions/compose';
|
||||
import { toggleFavourite, toggleReblog } from 'soapbox/actions/interactions';
|
||||
|
@ -23,10 +23,9 @@ import StatusReplyMentions from './status-reply-mentions';
|
|||
import SensitiveContentOverlay from './statuses/sensitive-content-overlay';
|
||||
import { Card, HStack, Stack, Text } from './ui';
|
||||
|
||||
import type { Map as ImmutableMap } from 'immutable';
|
||||
import type {
|
||||
Account as AccountEntity,
|
||||
// Group as GroupEntity,
|
||||
Group as GroupEntity,
|
||||
Status as StatusEntity,
|
||||
} from 'soapbox/types/entities';
|
||||
|
||||
|
@ -46,12 +45,12 @@ export interface IStatus {
|
|||
unread?: boolean,
|
||||
onMoveUp?: (statusId: string, featured?: boolean) => void,
|
||||
onMoveDown?: (statusId: string, featured?: boolean) => void,
|
||||
group?: ImmutableMap<string, any>,
|
||||
focusable?: boolean,
|
||||
featured?: boolean,
|
||||
hideActionBar?: boolean,
|
||||
hoverable?: boolean,
|
||||
variant?: 'default' | 'rounded',
|
||||
showGroup?: boolean,
|
||||
withDismiss?: boolean,
|
||||
accountAction?: React.ReactElement,
|
||||
}
|
||||
|
@ -70,6 +69,7 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
unread,
|
||||
hideActionBar,
|
||||
variant = 'rounded',
|
||||
showGroup = true,
|
||||
withDismiss,
|
||||
} = props;
|
||||
|
||||
|
@ -235,7 +235,7 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
const displayNameHtml = { __html: String(status.getIn(['account', 'display_name_html'])) };
|
||||
|
||||
reblogElement = (
|
||||
<NavLink
|
||||
<Link
|
||||
to={`/@${status.getIn(['account', 'acct'])}`}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
className='hidden sm:flex items-center text-gray-700 dark:text-gray-600 text-xs font-medium space-x-1 hover:underline'
|
||||
|
@ -253,12 +253,12 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
}}
|
||||
/>
|
||||
</HStack>
|
||||
</NavLink>
|
||||
</Link>
|
||||
);
|
||||
|
||||
reblogElementMobile = (
|
||||
<div className='pb-5 -mt-2 sm:hidden truncate'>
|
||||
<NavLink
|
||||
<Link
|
||||
to={`/@${status.getIn(['account', 'acct'])}`}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
className='flex items-center text-gray-700 dark:text-gray-600 text-xs font-medium space-x-1 hover:underline'
|
||||
|
@ -276,7 +276,7 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
}}
|
||||
/>
|
||||
</span>
|
||||
</NavLink>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -300,7 +300,7 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
}
|
||||
}
|
||||
|
||||
// const group = actualStatus.group as GroupEntity | null;
|
||||
const group = actualStatus.group as GroupEntity | null;
|
||||
|
||||
const handlers = muted ? undefined : {
|
||||
reply: handleHotkeyReply,
|
||||
|
@ -345,8 +345,18 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* {group && (
|
||||
<div className='pt-4 px-4'>
|
||||
<Card
|
||||
variant={variant}
|
||||
className={classNames('status__wrapper', `status-${actualStatus.visibility}`, {
|
||||
'py-6 sm:p-5': variant === 'rounded',
|
||||
'status-reply': !!status.in_reply_to_id,
|
||||
muted,
|
||||
read: unread === false,
|
||||
})}
|
||||
data-id={status.id}
|
||||
>
|
||||
{showGroup && group && (
|
||||
<div className='mb-4'>
|
||||
<HStack alignItems='center' space={1}>
|
||||
<Icon src={require('@tabler/icons/circles.svg')} className='text-gray-600 dark:text-gray-400' />
|
||||
|
||||
|
@ -363,18 +373,8 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
</Text>
|
||||
</HStack>
|
||||
</div>
|
||||
)} */}
|
||||
)}
|
||||
|
||||
<Card
|
||||
variant={variant}
|
||||
className={classNames('status__wrapper', `status-${actualStatus.visibility}`, {
|
||||
'py-6 sm:p-5': variant === 'rounded',
|
||||
'status-reply': !!status.in_reply_to_id,
|
||||
muted,
|
||||
read: unread === false,
|
||||
})}
|
||||
data-id={status.id}
|
||||
>
|
||||
{reblogElementMobile}
|
||||
|
||||
<div className='mb-4'>
|
||||
|
|
|
@ -64,7 +64,8 @@ const GroupTimeline: React.FC<IGroupTimeline> = (props) => {
|
|||
timelineId={`group:${groupId}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.group' defaultMessage='There are no posts in this group yet.' />}
|
||||
divideType='space'
|
||||
divideType='border'
|
||||
showGroup={false}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
@ -35,7 +35,6 @@ import {
|
|||
} from '../actions/timelines';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { StatusVisibility } from 'soapbox/normalizers/status';
|
||||
import type { APIEntity, Status } from 'soapbox/types/entities';
|
||||
|
||||
const TRUNCATE_LIMIT = 40;
|
||||
|
@ -242,8 +241,10 @@ const timelineDisconnect = (state: State, timelineId: string) => {
|
|||
}));
|
||||
};
|
||||
|
||||
const getTimelinesByVisibility = (visibility: StatusVisibility) => {
|
||||
switch (visibility) {
|
||||
const getTimelinesForStatus = (status: APIEntity) => {
|
||||
switch (status.visibility) {
|
||||
case 'group':
|
||||
return [`group:${status.group?.id || status.group_id}`];
|
||||
case 'direct':
|
||||
return ['direct'];
|
||||
case 'public':
|
||||
|
@ -269,7 +270,7 @@ const importPendingStatus = (state: State, params: APIEntity, idempotencyKey: st
|
|||
const statusId = `末pending-${idempotencyKey}`;
|
||||
|
||||
return state.withMutations(state => {
|
||||
const timelineIds = getTimelinesByVisibility(params.visibility);
|
||||
const timelineIds = getTimelinesForStatus(params);
|
||||
|
||||
timelineIds.forEach(timelineId => {
|
||||
updateTimelineQueue(state, timelineId, statusId);
|
||||
|
@ -293,7 +294,7 @@ const importStatus = (state: State, status: APIEntity, idempotencyKey: string) =
|
|||
return state.withMutations(state => {
|
||||
replacePendingStatus(state, idempotencyKey, status.id);
|
||||
|
||||
const timelineIds = getTimelinesByVisibility(status.visibility);
|
||||
const timelineIds = getTimelinesForStatus(status);
|
||||
|
||||
timelineIds.forEach(timelineId => {
|
||||
updateTimeline(state, timelineId, status.id);
|
||||
|
|
Loading…
Reference in New Issue