Support mutes duration
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
30ec3dbd9f
commit
ac83c9c18c
8 changed files with 95 additions and 6 deletions
|
@ -1,5 +1,5 @@
|
|||
import { isLoggedIn } from 'soapbox/utils/auth';
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features';
|
||||
|
||||
import api, { getLinks } from '../api';
|
||||
|
||||
|
@ -354,14 +354,30 @@ const unblockAccountFail = (error: AxiosError) => ({
|
|||
error,
|
||||
});
|
||||
|
||||
const muteAccount = (id: string, notifications?: boolean) =>
|
||||
const muteAccount = (id: string, notifications?: boolean, duration = 0) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return null;
|
||||
|
||||
dispatch(muteAccountRequest(id));
|
||||
|
||||
const params: Record<string, any> = {
|
||||
notifications,
|
||||
};
|
||||
|
||||
if (duration) {
|
||||
const state = getState();
|
||||
const instance = state.instance;
|
||||
const v = parseVersion(instance.version);
|
||||
|
||||
if (v.software === PLEROMA) {
|
||||
params.expires_in = duration;
|
||||
} else {
|
||||
params.duration = duration;
|
||||
}
|
||||
}
|
||||
|
||||
return api(getState)
|
||||
.post(`/api/v1/accounts/${id}/mute`, { notifications })
|
||||
.post(`/api/v1/accounts/${id}/mute`, params)
|
||||
.then(response => {
|
||||
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
||||
return dispatch(muteAccountSuccess(response.data, getState().statuses));
|
||||
|
|
|
@ -21,6 +21,7 @@ const MUTES_EXPAND_FAIL = 'MUTES_EXPAND_FAIL';
|
|||
|
||||
const MUTES_INIT_MODAL = 'MUTES_INIT_MODAL';
|
||||
const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS';
|
||||
const MUTES_CHANGE_DURATION = 'MUTES_CHANGE_DURATION';
|
||||
|
||||
const fetchMutes = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
|
@ -103,6 +104,14 @@ const toggleHideNotifications = () =>
|
|||
dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS });
|
||||
};
|
||||
|
||||
const changeMuteDuration = (duration: number) =>
|
||||
(dispatch: AppDispatch) => {
|
||||
dispatch({
|
||||
type: MUTES_CHANGE_DURATION,
|
||||
duration,
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
MUTES_FETCH_REQUEST,
|
||||
MUTES_FETCH_SUCCESS,
|
||||
|
@ -112,6 +121,7 @@ export {
|
|||
MUTES_EXPAND_FAIL,
|
||||
MUTES_INIT_MODAL,
|
||||
MUTES_TOGGLE_HIDE_NOTIFICATIONS,
|
||||
MUTES_CHANGE_DURATION,
|
||||
fetchMutes,
|
||||
fetchMutesRequest,
|
||||
fetchMutesSuccess,
|
||||
|
@ -122,4 +132,5 @@ export {
|
|||
expandMutesFail,
|
||||
initMuteModal,
|
||||
toggleHideNotifications,
|
||||
changeMuteDuration,
|
||||
};
|
||||
|
|
|
@ -235,6 +235,14 @@ const Account = ({
|
|||
<Icon className='h-5 w-5 stroke-[1.35]' src={require('@tabler/icons/pencil.svg')} />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{actionType === 'muting' && account.mute_expires_at ? (
|
||||
<>
|
||||
<Text tag='span' theme='muted' size='sm'>·</Text>
|
||||
|
||||
<Text theme='muted' size='sm'><RelativeTimestamp timestamp={account.mute_expires_at} futureDate /></Text>
|
||||
</>
|
||||
) : null}
|
||||
</HStack>
|
||||
|
||||
{withAccountNote && (
|
||||
|
|
|
@ -4,9 +4,10 @@ import Toggle from 'react-toggle';
|
|||
|
||||
import { muteAccount } from 'soapbox/actions/accounts';
|
||||
import { closeModal } from 'soapbox/actions/modals';
|
||||
import { toggleHideNotifications } from 'soapbox/actions/mutes';
|
||||
import { toggleHideNotifications, changeMuteDuration } from 'soapbox/actions/mutes';
|
||||
import { Modal, HStack, Stack, Text } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import DurationSelector from 'soapbox/features/compose/components/polls/duration-selector';
|
||||
import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
|
||||
const getAccount = makeGetAccount();
|
||||
|
@ -16,12 +17,14 @@ const MuteModal = () => {
|
|||
|
||||
const account = useAppSelector((state) => getAccount(state, state.mutes.new.accountId!));
|
||||
const notifications = useAppSelector((state) => state.mutes.new.notifications);
|
||||
const duration = useAppSelector((state) => state.mutes.new.duration);
|
||||
const mutesDuration = useFeatures().mutesDuration;
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
const handleClick = () => {
|
||||
dispatch(closeModal());
|
||||
dispatch(muteAccount(account.id, notifications));
|
||||
dispatch(muteAccount(account.id, notifications, duration));
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
|
@ -32,6 +35,12 @@ const MuteModal = () => {
|
|||
dispatch(toggleHideNotifications());
|
||||
};
|
||||
|
||||
const handleChangeMuteDuration = (expiresIn: number): void => {
|
||||
dispatch(changeMuteDuration(expiresIn));
|
||||
};
|
||||
|
||||
const toggleAutoExpire = () => handleChangeMuteDuration(duration ? 0 : 2 * 60 * 60 * 24);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={
|
||||
|
@ -69,6 +78,32 @@ const MuteModal = () => {
|
|||
/>
|
||||
</HStack>
|
||||
</label>
|
||||
|
||||
{mutesDuration && (
|
||||
<>
|
||||
<label>
|
||||
<HStack alignItems='center' space={2}>
|
||||
<Text tag='span'>
|
||||
<FormattedMessage id='mute_modal.auto_expire' defaultMessage='Automatically expire mute?' />
|
||||
</Text>
|
||||
|
||||
<Toggle
|
||||
checked={duration !== 0}
|
||||
onChange={toggleAutoExpire}
|
||||
icons={false}
|
||||
/>
|
||||
</HStack>
|
||||
</label>
|
||||
|
||||
{duration !== 0 && (
|
||||
<Stack space={2}>
|
||||
<Text weight='medium'><FormattedMessage id='mute_modal.duration' defaultMessage='Duration' />: </Text>
|
||||
|
||||
<DurationSelector onDurationChange={handleChangeMuteDuration} />
|
||||
</Stack>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -42,6 +42,7 @@ export const AccountRecord = ImmutableRecord({
|
|||
location: '',
|
||||
locked: false,
|
||||
moved: null as EmbeddedEntity<any>,
|
||||
mute_expires_at: null as Date | null,
|
||||
note: '',
|
||||
pleroma: ImmutableMap<string, any>(),
|
||||
source: ImmutableMap<string, any>(),
|
||||
|
|
|
@ -14,6 +14,7 @@ describe('mutes reducer', () => {
|
|||
isSubmitting: false,
|
||||
accountId: null,
|
||||
notifications: true,
|
||||
duration: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -24,6 +25,7 @@ describe('mutes reducer', () => {
|
|||
isSubmitting: false,
|
||||
accountId: null,
|
||||
notifications: true,
|
||||
duration: 0,
|
||||
})(),
|
||||
})();
|
||||
const action = {
|
||||
|
@ -35,6 +37,7 @@ describe('mutes reducer', () => {
|
|||
isSubmitting: false,
|
||||
accountId: 'account1',
|
||||
notifications: true,
|
||||
duration: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -45,6 +48,7 @@ describe('mutes reducer', () => {
|
|||
isSubmitting: false,
|
||||
accountId: null,
|
||||
notifications: true,
|
||||
duration: 0,
|
||||
})(),
|
||||
})();
|
||||
const action = {
|
||||
|
@ -55,6 +59,7 @@ describe('mutes reducer', () => {
|
|||
isSubmitting: false,
|
||||
accountId: null,
|
||||
notifications: false,
|
||||
duration: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Record as ImmutableRecord } from 'immutable';
|
|||
import {
|
||||
MUTES_INIT_MODAL,
|
||||
MUTES_TOGGLE_HIDE_NOTIFICATIONS,
|
||||
MUTES_CHANGE_DURATION,
|
||||
} from '../actions/mutes';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
@ -11,6 +12,7 @@ const NewMuteRecord = ImmutableRecord({
|
|||
isSubmitting: false,
|
||||
accountId: null,
|
||||
notifications: true,
|
||||
duration: 0,
|
||||
});
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
|
@ -29,6 +31,8 @@ export default function mutes(state: State = ReducerRecord(), action: AnyAction)
|
|||
});
|
||||
case MUTES_TOGGLE_HIDE_NOTIFICATIONS:
|
||||
return state.updateIn(['new', 'notifications'], (old) => !old);
|
||||
case MUTES_CHANGE_DURATION:
|
||||
return state.setIn(['new', 'duration'], action.duration);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -391,6 +391,15 @@ const getInstanceFeatures = (instance: Instance) => {
|
|||
*/
|
||||
muteStrangers: v.software === PLEROMA,
|
||||
|
||||
/**
|
||||
* Ability to specify how long the account mute should last.
|
||||
* @see PUT /api/v1/accounts/:id/mute
|
||||
*/
|
||||
mutesDuration: any([
|
||||
v.software === PLEROMA && gte(v.version, '2.3.0'),
|
||||
v.software === MASTODON && gte(v.compatVersion, '3.3.0'),
|
||||
]),
|
||||
|
||||
/**
|
||||
* Add private notes to accounts.
|
||||
* @see POST /api/v1/accounts/:id/note
|
||||
|
|
Loading…
Reference in a new issue