From f69d1d95e44916d964339f3d9314b650652257c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 6 Apr 2024 09:36:18 +0200 Subject: [PATCH] Support Mastodon rule hints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- src/actions/rules.test.ts | 23 ------------- src/actions/rules.ts | 32 ------------------- .../modals/report-modal/report-modal.tsx | 4 +-- .../report-modal/steps/other-actions-step.tsx | 7 +--- .../modals/report-modal/steps/reason-step.tsx | 14 +++----- src/reducers/index.ts | 2 -- src/reducers/rules.test.ts | 31 ------------------ src/reducers/rules.ts | 31 ------------------ src/schemas/rule.ts | 11 +++++-- 9 files changed, 17 insertions(+), 138 deletions(-) delete mode 100644 src/actions/rules.test.ts delete mode 100644 src/actions/rules.ts delete mode 100644 src/reducers/rules.test.ts delete mode 100644 src/reducers/rules.ts diff --git a/src/actions/rules.test.ts b/src/actions/rules.test.ts deleted file mode 100644 index 5fd32c6fe..000000000 --- a/src/actions/rules.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { __stub } from 'soapbox/api'; -import { mockStore, rootState } from 'soapbox/jest/test-helpers'; - -import { fetchRules, RULES_FETCH_REQUEST, RULES_FETCH_SUCCESS } from './rules'; - -describe('fetchRules()', () => { - it('sets the rules', async () => { - const rules = await import('soapbox/__fixtures__/rules.json'); - - __stub((mock) => { - mock.onGet('/api/v1/instance/rules').reply(200, rules); - }); - - const store = mockStore(rootState); - await store.dispatch(fetchRules()); - - const actions = store.getActions(); - - expect(actions[0].type).toEqual(RULES_FETCH_REQUEST); - expect(actions[1].type).toEqual(RULES_FETCH_SUCCESS); - expect(actions[1].payload[0].id).toEqual('1'); - }); -}); diff --git a/src/actions/rules.ts b/src/actions/rules.ts deleted file mode 100644 index 242d0bbda..000000000 --- a/src/actions/rules.ts +++ /dev/null @@ -1,32 +0,0 @@ -import api from '../api'; - -import type { Rule } from 'soapbox/reducers/rules'; -import type { RootState } from 'soapbox/store'; - -const RULES_FETCH_REQUEST = 'RULES_FETCH_REQUEST'; -const RULES_FETCH_SUCCESS = 'RULES_FETCH_SUCCESS'; - -type RulesFetchRequestAction = { - type: typeof RULES_FETCH_REQUEST; -} - -type RulesFetchRequestSuccessAction = { - type: typeof RULES_FETCH_SUCCESS; - payload: Rule[]; -} - -export type RulesActions = RulesFetchRequestAction | RulesFetchRequestSuccessAction - -const fetchRules = () => (dispatch: React.Dispatch, getState: () => RootState) => { - dispatch({ type: RULES_FETCH_REQUEST }); - - return api(getState) - .get('/api/v1/instance/rules') - .then((response) => dispatch({ type: RULES_FETCH_SUCCESS, payload: response.data })); -}; - -export { - fetchRules, - RULES_FETCH_REQUEST, - RULES_FETCH_SUCCESS, -}; diff --git a/src/features/ui/components/modals/report-modal/report-modal.tsx b/src/features/ui/components/modals/report-modal/report-modal.tsx index ce961d0fb..77fbb1946 100644 --- a/src/features/ui/components/modals/report-modal/report-modal.tsx +++ b/src/features/ui/components/modals/report-modal/report-modal.tsx @@ -11,7 +11,7 @@ import List, { ListItem } from 'soapbox/components/list'; import StatusContent from 'soapbox/components/status-content'; import { Avatar, HStack, Icon, Modal, ProgressBar, Stack, Text } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account-container'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useInstance } from 'soapbox/hooks'; import ConfirmationStep from './steps/confirmation-step'; import OtherActionsStep from './steps/other-actions-step'; @@ -104,7 +104,7 @@ const ReportModal = ({ onClose }: IReportModal) => { const entityType = useAppSelector((state) => state.reports.new.entityType); const isBlocked = useAppSelector((state) => state.reports.new.block); const isSubmitting = useAppSelector((state) => state.reports.new.isSubmitting); - const rules = useAppSelector((state) => state.rules.items); + const { rules } = useInstance(); const ruleIds = useAppSelector((state) => state.reports.new.rule_ids); const selectedStatusIds = useAppSelector((state) => state.reports.new.status_ids); const selectedChatMessage = useAppSelector((state) => state.reports.new.chat_message); diff --git a/src/features/ui/components/modals/report-modal/steps/other-actions-step.tsx b/src/features/ui/components/modals/report-modal/steps/other-actions-step.tsx index 8aaf7734c..67a56d66b 100644 --- a/src/features/ui/components/modals/report-modal/steps/other-actions-step.tsx +++ b/src/features/ui/components/modals/report-modal/steps/other-actions-step.tsx @@ -1,9 +1,8 @@ import { OrderedSet } from 'immutable'; -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { changeReportBlock, changeReportForward } from 'soapbox/actions/reports'; -import { fetchRules } from 'soapbox/actions/rules'; import { Button, FormGroup, HStack, Stack, Text, Toggle } from 'soapbox/components/ui'; import StatusCheckBox from 'soapbox/features/report/components/status-check-box'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; @@ -44,10 +43,6 @@ const OtherActionsStep = ({ account }: IOtherActionsStep) => { dispatch(changeReportForward(event.target.checked)); }; - useEffect(() => { - dispatch(fetchRules()); - }, []); - return ( {features.reportMultipleStatuses && ( diff --git a/src/features/ui/components/modals/report-modal/steps/reason-step.tsx b/src/features/ui/components/modals/report-modal/steps/reason-step.tsx index 9a602916b..7703ab995 100644 --- a/src/features/ui/components/modals/report-modal/steps/reason-step.tsx +++ b/src/features/ui/components/modals/report-modal/steps/reason-step.tsx @@ -3,11 +3,11 @@ import React, { useEffect, useRef, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { changeReportComment, changeReportRule, ReportableEntities } from 'soapbox/actions/reports'; -import { fetchRules } from 'soapbox/actions/rules'; import { FormGroup, Stack, Text, Textarea } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useInstance } from 'soapbox/hooks'; import type { Account } from 'soapbox/schemas'; +import type { Rule } from 'soapbox/schemas/rule'; const messages = defineMessages({ placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' }, @@ -31,7 +31,7 @@ const ReasonStep: React.FC = () => { const entityType = useAppSelector((state) => state.reports.new.entityType); const comment = useAppSelector((state) => state.reports.new.comment); - const rules = useAppSelector((state) => state.rules.items); + const { rules } = useInstance(); const ruleIds = useAppSelector((state) => state.reports.new.rule_ids); const shouldRequireRule = rules.length > 0; @@ -57,7 +57,7 @@ const ReasonStep: React.FC = () => { } }; - const filterRuleType = (rule: any) => { + const filterRuleType = (rule: Rule) => { let ruleTypeToFilter = 'content'; switch (entityType) { @@ -83,10 +83,6 @@ const ReasonStep: React.FC = () => { return true; }; - useEffect(() => { - dispatch(fetchRules()); - }, []); - useEffect(() => { if (rules.length > 0 && rulesListRef.current) { const { clientHeight } = rulesListRef.current; @@ -136,7 +132,7 @@ const ReasonStep: React.FC = () => { > {rule.text} - {rule.subtext} + {rule.hint} { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(initialState); - }); - - describe('RULES_FETCH_REQUEST', () => { - it('sets "needsOnboarding" to "true"', () => { - const action = { type: RULES_FETCH_REQUEST } as any; - expect(reducer(initialState, action).isLoading).toEqual(true); - }); - }); - - describe('ONBOARDING_END', () => { - it('sets "needsOnboarding" to "false"', () => { - const action = { type: RULES_FETCH_SUCCESS, payload: [{ id: '123' }] } as any; - const result = reducer(initialState, action); - expect(result.isLoading).toEqual(false); - expect(result.items[0].id).toEqual('123'); - }); - }); -}); diff --git a/src/reducers/rules.ts b/src/reducers/rules.ts deleted file mode 100644 index 68394d2e2..000000000 --- a/src/reducers/rules.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { RULES_FETCH_REQUEST, RULES_FETCH_SUCCESS } from '../actions/rules'; - -import type { RulesActions } from '../actions/rules'; - -export type Rule = { - id: string; - text: string; - subtext: string; - rule_type: 'content' | 'account'; -} - -type RulesState = { - items: Rule[]; - isLoading: boolean; -} - -const initialState: RulesState = { - items: [], - isLoading: false, -}; - -export default function rules(state: RulesState = initialState, action: RulesActions): RulesState { - switch (action.type) { - case RULES_FETCH_REQUEST: - return { ...state, isLoading: true }; - case RULES_FETCH_SUCCESS: - return { ...state, isLoading: false, items: action.payload }; - default: - return state; - } -} diff --git a/src/schemas/rule.ts b/src/schemas/rule.ts index 32f949b08..84f91e991 100644 --- a/src/schemas/rule.ts +++ b/src/schemas/rule.ts @@ -2,10 +2,17 @@ import { z } from 'zod'; import { coerceObject } from './utils'; -const ruleSchema = coerceObject({ +const ruleSchema = z.preprocess((data: any) => { + return { + ...data, + hint: data.hint || data.subtext, + }; +}, coerceObject({ id: z.string(), text: z.string().catch(''), -}); + hint: z.string().catch(''), + rule_type: z.enum(['account', 'content', 'group']).nullable().catch(null), +})); type Rule = z.infer;