From 0f4058ea13b45aa8a5414627c8dd80cf63bc422d Mon Sep 17 00:00:00 2001 From: plebeius Date: Sat, 2 Aug 2025 23:45:00 +0200 Subject: [PATCH] feat(subplebbit settings): add json editor --- public/translations/en/default.json | 2 +- src/app.tsx | 2 + src/lib/utils/view-utils.ts | 2 +- .../subplebbit-data-editor/index.ts | 1 + .../subplebbit-data-editor.tsx | 211 ++++++++++++++++++ .../subplebbit-settings.tsx | 21 +- 6 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 src/views/subplebbit-settings/subplebbit-data-editor/index.ts create mode 100644 src/views/subplebbit-settings/subplebbit-data-editor/subplebbit-data-editor.tsx diff --git a/public/translations/en/default.json b/public/translations/en/default.json index b1395741..2bdd7662 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -224,7 +224,7 @@ "add_moderator": "add a moderator", "add_rule": "add a rule", "json_settings": "JSON settings", - "json_settings_info": "quickly copy or paste the community settings", + "json_settings_info": "quickly copy and paste the community settings", "address_setting_info": "Set a readable community address using a crypto domain", "enter_crypto_address": "Please enter a valid crypto address.", "check_for_updates": "<1>Check for updates", diff --git a/src/app.tsx b/src/app.tsx index 87f8fa66..6c59b10c 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -15,6 +15,7 @@ import PostPage from './views/post-page'; import Profile from './views/profile'; import Settings from './views/settings'; import AccountDataEditor from './views/settings/account-data-editor'; +import SubplebbitDataEditor from './views/subplebbit-settings/subplebbit-data-editor'; import SubmitPage from './views/submit-page'; import Subplebbit from './views/subplebbit'; import SubplebbitSettings from './views/subplebbit-settings'; @@ -97,6 +98,7 @@ const App = () => { } /> } /> + } /> } /> } /> } /> diff --git a/src/lib/utils/view-utils.ts b/src/lib/utils/view-utils.ts index 1bbce1d3..9906a683 100644 --- a/src/lib/utils/view-utils.ts +++ b/src/lib/utils/view-utils.ts @@ -176,7 +176,7 @@ export const isSubplebbitAboutView = (pathname: string, params: ParamsType): boo }; export const isSubplebbitSettingsView = (pathname: string, params: ParamsType): boolean => { - return params.subplebbitAddress ? pathname === `/p/${params.subplebbitAddress}/settings` : false; + return params.subplebbitAddress ? pathname === `/p/${params.subplebbitAddress}/settings` || pathname === `/p/${params.subplebbitAddress}/settings/editor` : false; }; export const isSubplebbitSubmitView = (pathname: string, params: ParamsType): boolean => { diff --git a/src/views/subplebbit-settings/subplebbit-data-editor/index.ts b/src/views/subplebbit-settings/subplebbit-data-editor/index.ts new file mode 100644 index 00000000..5edffcdf --- /dev/null +++ b/src/views/subplebbit-settings/subplebbit-data-editor/index.ts @@ -0,0 +1 @@ +export { default } from './subplebbit-data-editor'; diff --git a/src/views/subplebbit-settings/subplebbit-data-editor/subplebbit-data-editor.tsx b/src/views/subplebbit-settings/subplebbit-data-editor/subplebbit-data-editor.tsx new file mode 100644 index 00000000..87e90c18 --- /dev/null +++ b/src/views/subplebbit-settings/subplebbit-data-editor/subplebbit-data-editor.tsx @@ -0,0 +1,211 @@ +import React, { useEffect, useMemo, useState, lazy, Suspense, Component } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { usePublishSubplebbitEdit, useSubplebbit } from '@plebbit/plebbit-react-hooks'; +import useTheme from '../../../stores/use-theme-store'; +import styles from '../../settings/account-data-editor/account-data-editor.module.css'; +import useIsMobile from '../../../hooks/use-is-mobile'; +import LoadingEllipsis from '../../../components/loading-ellipsis'; +import useSubplebbitSettingsStore from '../../../stores/use-subplebbit-settings-store'; +import { useParams } from 'react-router-dom'; +import ErrorDisplay from '../../../components/error-display'; +import useStateString from '../../../hooks/use-state-string'; + +class EditorErrorBoundary extends Component<{ children: React.ReactNode; fallback: React.ReactNode }> { + constructor(props: { children: React.ReactNode; fallback: React.ReactNode }) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError() { + return { hasError: true }; + } + + componentDidCatch(error: any, errorInfo: any) { + console.error('Ace Editor failed to load:', error, errorInfo); + } + + render() { + if ((this.state as any).hasError) { + return this.props.fallback; + } + return this.props.children; + } +} + +const LazyAceEditor = lazy(async () => { + const ReactAceModule = await import('react-ace'); + await import('ace-builds/src-noconflict/mode-json'); + await import('ace-builds/src-noconflict/theme-github'); + await import('ace-builds/src-noconflict/theme-tomorrow_night'); + return ReactAceModule; +}); + +const FallbackEditor = ({ value, onChange, height, disabled }: { value: string; onChange: (value: string) => void; height: string; disabled?: boolean }) => { + const { t } = useTranslation(); + + return ( +
+
{t('editor_fallback_warning', 'Advanced editor failed to load. Using basic text editor as fallback.')}
+