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/components/status-action-bar.tsx b/src/components/status-action-bar.tsx index 2d7628fd3..31136b4d9 100644 --- a/src/components/status-action-bar.tsx +++ b/src/components/status-action-bar.tsx @@ -401,7 +401,6 @@ const StatusActionBar: React.FC = ({ const ownAccount = status.account.id === me; const username = status.account.username; const account = status.account; - const domain = account.fqn.split('@')[1]; const menu: Menu = []; @@ -455,6 +454,7 @@ const StatusActionBar: React.FC = ({ } if (features.federating && !account.local) { + const { hostname: domain } = new URL(status.uri); menu.push({ text: intl.formatMessage(messages.external, { domain }), action: handleExternalClick, diff --git a/src/entity-store/hooks/useEntityLookup.ts b/src/entity-store/hooks/useEntityLookup.ts index a54882d6f..3ae97b704 100644 --- a/src/entity-store/hooks/useEntityLookup.ts +++ b/src/entity-store/hooks/useEntityLookup.ts @@ -25,10 +25,11 @@ function useEntityLookup( const { schema = z.custom() } = opts; const dispatch = useAppDispatch(); + const [fetchedEntity, setFetchedEntity] = useState(); const [isFetching, setPromise] = useLoading(true); const [error, setError] = useState(); - const entity = useAppSelector(state => findEntity(state, entityType, lookupFn)); + const entity = useAppSelector(state => findEntity(state, entityType, lookupFn) ?? fetchedEntity); const isEnabled = opts.enabled ?? true; const isLoading = isFetching && !entity; @@ -36,6 +37,7 @@ function useEntityLookup( try { const response = await setPromise(entityFn()); const entity = schema.parse(response.data); + setFetchedEntity(entity); dispatch(importEntities([entity], entityType)); } catch (e) { setError(e); diff --git a/src/features/nostr/Bech32Redirect.tsx b/src/features/nostr/Bech32Redirect.tsx new file mode 100644 index 000000000..4c9fdb16b --- /dev/null +++ b/src/features/nostr/Bech32Redirect.tsx @@ -0,0 +1,34 @@ +import { nip19 } from 'nostr-tools'; +import React from 'react'; +import { Redirect } from 'react-router-dom'; + +import MissingIndicator from 'soapbox/components/missing-indicator'; + +interface INIP19Redirect { + params: { + bech32: string; + }; +} + +const Bech32Redirect: React.FC = ({ params }) => { + try { + const result = nip19.decode(params.bech32); + + switch (result.type) { + case 'npub': + case 'nprofile': + return ; + case 'note': + return ; + case 'nevent': + return ; + default: + return ; + } + + } catch (e) { + return ; + } +}; + +export default Bech32Redirect; \ No newline at end of file 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} = ({ children }) => {features.events && } {features.events && } + {features.groups && } {features.groupsDiscovery && } @@ -363,6 +365,8 @@ const SwitchingColumnsArea: React.FC = ({ children }) => + + ); diff --git a/src/features/ui/util/async-components.ts b/src/features/ui/util/async-components.ts index b156148a4..7f9227c99 100644 --- a/src/features/ui/util/async-components.ts +++ b/src/features/ui/util/async-components.ts @@ -171,4 +171,5 @@ export const EditIdentity = lazy(() => import('soapbox/features/edit-identity')) export const Domains = lazy(() => import('soapbox/features/admin/domains')); export const EditDomainModal = lazy(() => import('soapbox/features/ui/components/modals/edit-domain-modal')); export const NostrRelays = lazy(() => import('soapbox/features/nostr-relays')); +export const Bech32Redirect = lazy(() => import('soapbox/features/nostr/Bech32Redirect')); export const Relays = lazy(() => import('soapbox/features/admin/relays')); diff --git a/src/reducers/index.ts b/src/reducers/index.ts index c8ea0e81a..d0e71d428 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -47,7 +47,6 @@ import profile_hover_card from './profile-hover-card'; import push_notifications from './push-notifications'; import relationships from './relationships'; import reports from './reports'; -import rules from './rules'; import scheduled_statuses from './scheduled-statuses'; import search from './search'; import security from './security'; @@ -108,7 +107,6 @@ const reducers = { push_notifications, relationships, reports, - rules, scheduled_statuses, search, security, diff --git a/src/reducers/rules.test.ts b/src/reducers/rules.test.ts deleted file mode 100644 index 5c2d4b354..000000000 --- a/src/reducers/rules.test.ts +++ /dev/null @@ -1,31 +0,0 @@ - -import { RULES_FETCH_REQUEST, RULES_FETCH_SUCCESS } from 'soapbox/actions/rules'; - -import reducer from './rules'; - -const initialState = { - items: [], - isLoading: false, -}; - -describe('rules reducer', () => { - 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;