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 { isLoggedIn } from 'soapbox/utils/auth';
|
||||||
import { getFeatures } from 'soapbox/utils/features';
|
import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features';
|
||||||
|
|
||||||
import api, { getLinks } from '../api';
|
import api, { getLinks } from '../api';
|
||||||
|
|
||||||
|
@ -354,14 +354,30 @@ const unblockAccountFail = (error: AxiosError) => ({
|
||||||
error,
|
error,
|
||||||
});
|
});
|
||||||
|
|
||||||
const muteAccount = (id: string, notifications?: boolean) =>
|
const muteAccount = (id: string, notifications?: boolean, duration = 0) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
if (!isLoggedIn(getState)) return null;
|
if (!isLoggedIn(getState)) return null;
|
||||||
|
|
||||||
dispatch(muteAccountRequest(id));
|
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)
|
return api(getState)
|
||||||
.post(`/api/v1/accounts/${id}/mute`, { notifications })
|
.post(`/api/v1/accounts/${id}/mute`, params)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
// 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));
|
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_INIT_MODAL = 'MUTES_INIT_MODAL';
|
||||||
const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS';
|
const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS';
|
||||||
|
const MUTES_CHANGE_DURATION = 'MUTES_CHANGE_DURATION';
|
||||||
|
|
||||||
const fetchMutes = () =>
|
const fetchMutes = () =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
@ -103,6 +104,14 @@ const toggleHideNotifications = () =>
|
||||||
dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS });
|
dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const changeMuteDuration = (duration: number) =>
|
||||||
|
(dispatch: AppDispatch) => {
|
||||||
|
dispatch({
|
||||||
|
type: MUTES_CHANGE_DURATION,
|
||||||
|
duration,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
MUTES_FETCH_REQUEST,
|
MUTES_FETCH_REQUEST,
|
||||||
MUTES_FETCH_SUCCESS,
|
MUTES_FETCH_SUCCESS,
|
||||||
|
@ -112,6 +121,7 @@ export {
|
||||||
MUTES_EXPAND_FAIL,
|
MUTES_EXPAND_FAIL,
|
||||||
MUTES_INIT_MODAL,
|
MUTES_INIT_MODAL,
|
||||||
MUTES_TOGGLE_HIDE_NOTIFICATIONS,
|
MUTES_TOGGLE_HIDE_NOTIFICATIONS,
|
||||||
|
MUTES_CHANGE_DURATION,
|
||||||
fetchMutes,
|
fetchMutes,
|
||||||
fetchMutesRequest,
|
fetchMutesRequest,
|
||||||
fetchMutesSuccess,
|
fetchMutesSuccess,
|
||||||
|
@ -122,4 +132,5 @@ export {
|
||||||
expandMutesFail,
|
expandMutesFail,
|
||||||
initMuteModal,
|
initMuteModal,
|
||||||
toggleHideNotifications,
|
toggleHideNotifications,
|
||||||
|
changeMuteDuration,
|
||||||
};
|
};
|
||||||
|
|
|
@ -235,6 +235,14 @@ const Account = ({
|
||||||
<Icon className='h-5 w-5 stroke-[1.35]' src={require('@tabler/icons/pencil.svg')} />
|
<Icon className='h-5 w-5 stroke-[1.35]' src={require('@tabler/icons/pencil.svg')} />
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : 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>
|
</HStack>
|
||||||
|
|
||||||
{withAccountNote && (
|
{withAccountNote && (
|
||||||
|
|
|
@ -4,9 +4,10 @@ import Toggle from 'react-toggle';
|
||||||
|
|
||||||
import { muteAccount } from 'soapbox/actions/accounts';
|
import { muteAccount } from 'soapbox/actions/accounts';
|
||||||
import { closeModal } from 'soapbox/actions/modals';
|
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 { 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';
|
import { makeGetAccount } from 'soapbox/selectors';
|
||||||
|
|
||||||
const getAccount = makeGetAccount();
|
const getAccount = makeGetAccount();
|
||||||
|
@ -16,12 +17,14 @@ const MuteModal = () => {
|
||||||
|
|
||||||
const account = useAppSelector((state) => getAccount(state, state.mutes.new.accountId!));
|
const account = useAppSelector((state) => getAccount(state, state.mutes.new.accountId!));
|
||||||
const notifications = useAppSelector((state) => state.mutes.new.notifications);
|
const notifications = useAppSelector((state) => state.mutes.new.notifications);
|
||||||
|
const duration = useAppSelector((state) => state.mutes.new.duration);
|
||||||
|
const mutesDuration = useFeatures().mutesDuration;
|
||||||
|
|
||||||
if (!account) return null;
|
if (!account) return null;
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
dispatch(closeModal());
|
dispatch(closeModal());
|
||||||
dispatch(muteAccount(account.id, notifications));
|
dispatch(muteAccount(account.id, notifications, duration));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
|
@ -32,6 +35,12 @@ const MuteModal = () => {
|
||||||
dispatch(toggleHideNotifications());
|
dispatch(toggleHideNotifications());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChangeMuteDuration = (expiresIn: number): void => {
|
||||||
|
dispatch(changeMuteDuration(expiresIn));
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleAutoExpire = () => handleChangeMuteDuration(duration ? 0 : 2 * 60 * 60 * 24);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={
|
title={
|
||||||
|
@ -69,6 +78,32 @@ const MuteModal = () => {
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
</label>
|
</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>
|
</Stack>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -42,6 +42,7 @@ export const AccountRecord = ImmutableRecord({
|
||||||
location: '',
|
location: '',
|
||||||
locked: false,
|
locked: false,
|
||||||
moved: null as EmbeddedEntity<any>,
|
moved: null as EmbeddedEntity<any>,
|
||||||
|
mute_expires_at: null as Date | null,
|
||||||
note: '',
|
note: '',
|
||||||
pleroma: ImmutableMap<string, any>(),
|
pleroma: ImmutableMap<string, any>(),
|
||||||
source: ImmutableMap<string, any>(),
|
source: ImmutableMap<string, any>(),
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe('mutes reducer', () => {
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
accountId: null,
|
accountId: null,
|
||||||
notifications: true,
|
notifications: true,
|
||||||
|
duration: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -24,6 +25,7 @@ describe('mutes reducer', () => {
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
accountId: null,
|
accountId: null,
|
||||||
notifications: true,
|
notifications: true,
|
||||||
|
duration: 0,
|
||||||
})(),
|
})(),
|
||||||
})();
|
})();
|
||||||
const action = {
|
const action = {
|
||||||
|
@ -35,6 +37,7 @@ describe('mutes reducer', () => {
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
accountId: 'account1',
|
accountId: 'account1',
|
||||||
notifications: true,
|
notifications: true,
|
||||||
|
duration: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -45,6 +48,7 @@ describe('mutes reducer', () => {
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
accountId: null,
|
accountId: null,
|
||||||
notifications: true,
|
notifications: true,
|
||||||
|
duration: 0,
|
||||||
})(),
|
})(),
|
||||||
})();
|
})();
|
||||||
const action = {
|
const action = {
|
||||||
|
@ -55,6 +59,7 @@ describe('mutes reducer', () => {
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
accountId: null,
|
accountId: null,
|
||||||
notifications: false,
|
notifications: false,
|
||||||
|
duration: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Record as ImmutableRecord } from 'immutable';
|
||||||
import {
|
import {
|
||||||
MUTES_INIT_MODAL,
|
MUTES_INIT_MODAL,
|
||||||
MUTES_TOGGLE_HIDE_NOTIFICATIONS,
|
MUTES_TOGGLE_HIDE_NOTIFICATIONS,
|
||||||
|
MUTES_CHANGE_DURATION,
|
||||||
} from '../actions/mutes';
|
} from '../actions/mutes';
|
||||||
|
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
|
@ -11,6 +12,7 @@ const NewMuteRecord = ImmutableRecord({
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
accountId: null,
|
accountId: null,
|
||||||
notifications: true,
|
notifications: true,
|
||||||
|
duration: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const ReducerRecord = ImmutableRecord({
|
const ReducerRecord = ImmutableRecord({
|
||||||
|
@ -29,6 +31,8 @@ export default function mutes(state: State = ReducerRecord(), action: AnyAction)
|
||||||
});
|
});
|
||||||
case MUTES_TOGGLE_HIDE_NOTIFICATIONS:
|
case MUTES_TOGGLE_HIDE_NOTIFICATIONS:
|
||||||
return state.updateIn(['new', 'notifications'], (old) => !old);
|
return state.updateIn(['new', 'notifications'], (old) => !old);
|
||||||
|
case MUTES_CHANGE_DURATION:
|
||||||
|
return state.setIn(['new', 'duration'], action.duration);
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -391,6 +391,15 @@ const getInstanceFeatures = (instance: Instance) => {
|
||||||
*/
|
*/
|
||||||
muteStrangers: v.software === PLEROMA,
|
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.
|
* Add private notes to accounts.
|
||||||
* @see POST /api/v1/accounts/:id/note
|
* @see POST /api/v1/accounts/:id/note
|
||||||
|
|
Loading…
Reference in a new issue