From ac83c9c18c9c75a963f77cd45dad13117975e586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 31 Jul 2022 11:57:31 +0200 Subject: [PATCH] Support mutes duration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/accounts.ts | 22 ++++++++-- app/soapbox/actions/mutes.ts | 11 +++++ app/soapbox/components/account.tsx | 8 ++++ .../features/ui/components/mute_modal.tsx | 41 +++++++++++++++++-- app/soapbox/normalizers/account.ts | 1 + app/soapbox/reducers/__tests__/mutes.test.ts | 5 +++ app/soapbox/reducers/mutes.ts | 4 ++ app/soapbox/utils/features.ts | 9 ++++ 8 files changed, 95 insertions(+), 6 deletions(-) diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index 0b7f4e0cc..de6f78884 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -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 = { + 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)); diff --git a/app/soapbox/actions/mutes.ts b/app/soapbox/actions/mutes.ts index 050a513f0..bb684b0d6 100644 --- a/app/soapbox/actions/mutes.ts +++ b/app/soapbox/actions/mutes.ts @@ -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, }; diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx index fc4b290b6..183731297 100644 --- a/app/soapbox/components/account.tsx +++ b/app/soapbox/components/account.tsx @@ -235,6 +235,14 @@ const Account = ({ ) : null} + + {actionType === 'muting' && account.mute_expires_at ? ( + <> + · + + + + ) : null} {withAccountNote && ( diff --git a/app/soapbox/features/ui/components/mute_modal.tsx b/app/soapbox/features/ui/components/mute_modal.tsx index b2b5e25b2..32909c5db 100644 --- a/app/soapbox/features/ui/components/mute_modal.tsx +++ b/app/soapbox/features/ui/components/mute_modal.tsx @@ -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 ( { /> + + {mutesDuration && ( + <> + + + {duration !== 0 && ( + + : + + + + )} + + )} ); diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index 1a519b8a8..830b51953 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -42,6 +42,7 @@ export const AccountRecord = ImmutableRecord({ location: '', locked: false, moved: null as EmbeddedEntity, + mute_expires_at: null as Date | null, note: '', pleroma: ImmutableMap(), source: ImmutableMap(), diff --git a/app/soapbox/reducers/__tests__/mutes.test.ts b/app/soapbox/reducers/__tests__/mutes.test.ts index 66a866958..27d38c002 100644 --- a/app/soapbox/reducers/__tests__/mutes.test.ts +++ b/app/soapbox/reducers/__tests__/mutes.test.ts @@ -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, }, }); }); diff --git a/app/soapbox/reducers/mutes.ts b/app/soapbox/reducers/mutes.ts index e232d4039..4c0b08c39 100644 --- a/app/soapbox/reducers/mutes.ts +++ b/app/soapbox/reducers/mutes.ts @@ -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; } diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index e3683def3..43c5bea88 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -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