From 829bbc7f48184e4e77d90d7f82c81d9ca0030aec Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 13 Jan 2023 21:37:18 -0600 Subject: [PATCH] FeaturesEditor: allow saving changes --- .../features/admin/features-editor.tsx | 60 +++++++++++++++++++ .../features/developers/features-editor.tsx | 21 ------- app/soapbox/features/features-panel/index.tsx | 13 +++- app/soapbox/features/ui/index.tsx | 6 +- .../features/ui/util/async-components.ts | 8 +-- .../normalizers/soapbox/soapbox-config.ts | 2 + 6 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 app/soapbox/features/admin/features-editor.tsx delete mode 100644 app/soapbox/features/developers/features-editor.tsx diff --git a/app/soapbox/features/admin/features-editor.tsx b/app/soapbox/features/admin/features-editor.tsx new file mode 100644 index 000000000..c883407bb --- /dev/null +++ b/app/soapbox/features/admin/features-editor.tsx @@ -0,0 +1,60 @@ +import { Map as ImmutableMap } from 'immutable'; +import React, { useState } from 'react'; +import { defineMessages, useIntl } from 'react-intl'; + +import { updateSoapboxConfig } from 'soapbox/actions/admin'; +import { getHost } from 'soapbox/actions/instance'; +import { fetchSoapboxConfig } from 'soapbox/actions/soapbox'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import toast from 'soapbox/toast'; + +import FeaturesPanel from '../features-panel'; + +const messages = defineMessages({ + saved: { id: 'features_editor.saved', defaultMessage: 'Features updated!' }, +}); + +interface IFeaturesEditor { +} + +/** Admin feature editor. */ +const FeaturesEditor: React.FC = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + const [submitting, setSubmitting] = useState(false); + + const host = useAppSelector(state => getHost(state)); + const rawConfig = useAppSelector(state => state.soapbox); + + const userFeatures: Record = useAppSelector(state => { + return (state.soapbox.get('features') || ImmutableMap()).toJS(); + }); + + const updateFeatures = async (features: Record) => { + const params = rawConfig.set('features', features).toJS(); + await dispatch(updateSoapboxConfig(params)); + }; + + const handleChange = async(features: Record) => { + setSubmitting(true); + + try { + await dispatch(fetchSoapboxConfig(host)); + await updateFeatures(features); + toast.success(intl.formatMessage(messages.saved)); + setSubmitting(false); + } catch (e) { + setSubmitting(false); + } + }; + + return ( + + ); +}; + +export default FeaturesEditor; \ No newline at end of file diff --git a/app/soapbox/features/developers/features-editor.tsx b/app/soapbox/features/developers/features-editor.tsx deleted file mode 100644 index 6b7568683..000000000 --- a/app/soapbox/features/developers/features-editor.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; -import React from 'react'; - -import { useSettings } from 'soapbox/hooks'; - -import FeaturesPanel from '../features-panel'; - -interface IFeaturesEditor { -} - -/** Developers feature editor. */ -const FeaturesEditor: React.FC = () => { - const settings = useSettings(); - const features = (settings.get('features') || ImmutableMap()).toJS() as Record; - - return ( - - ); -}; - -export default FeaturesEditor; \ No newline at end of file diff --git a/app/soapbox/features/features-panel/index.tsx b/app/soapbox/features/features-panel/index.tsx index 5602edf61..37d6efc0a 100644 --- a/app/soapbox/features/features-panel/index.tsx +++ b/app/soapbox/features/features-panel/index.tsx @@ -12,10 +12,15 @@ const messages = defineMessages({ interface IFeaturesPanel { features: Record, onChange?: (features: Record) => void, + disabled?: boolean, } /** A UI for managing conditional feature flags. */ -const FeaturesPanel: React.FC = ({ features: userFeatures, onChange }) => { +const FeaturesPanel: React.FC = ({ + features: userFeatures, + onChange, + disabled = false, +}) => { const intl = useIntl(); const autoFeatures = useFeatures(); @@ -36,7 +41,11 @@ const FeaturesPanel: React.FC = ({ features: userFeatures, onCha {Object.keys(autoFeatures).map(key => ( - + ))} diff --git a/app/soapbox/features/ui/index.tsx b/app/soapbox/features/ui/index.tsx index 0a1b10853..706b0a085 100644 --- a/app/soapbox/features/ui/index.tsx +++ b/app/soapbox/features/ui/index.tsx @@ -109,7 +109,7 @@ import { ThemeEditor, Quotes, ServiceWorkerInfo, - DevelopersFeaturesEditor, + FeaturesEditor, EventInformation, EventDiscussion, Events, @@ -297,13 +297,13 @@ const SwitchingColumnsArea: React.FC = ({ children }) => - + + - new Promise((_resolve, reject) => reject())} content={children} /> diff --git a/app/soapbox/features/ui/util/async-components.ts b/app/soapbox/features/ui/util/async-components.ts index 190efe1c8..abfb2cccd 100644 --- a/app/soapbox/features/ui/util/async-components.ts +++ b/app/soapbox/features/ui/util/async-components.ts @@ -310,6 +310,10 @@ export function ModerationLog() { return import(/* webpackChunkName: "features/admin/moderation_log" */'../../admin/moderation-log'); } +export function FeaturesEditor() { + return import(/* webpackChunkName: "features/admin" */'../../admin/features-editor'); +} + export function ThemeEditor() { return import(/* webpackChunkName: "features/theme-editor" */'../../theme-editor'); } @@ -474,10 +478,6 @@ export function ServiceWorkerInfo() { return import(/* webpackChunkName: "features/developers" */'../../developers/service-worker-info'); } -export function DevelopersFeaturesEditor() { - return import(/* webpackChunkName: "features/developers" */'../../developers/features-editor'); -} - export function DatePicker() { return import(/* webpackChunkName: "date_picker" */'../../birthdays/date-picker'); } diff --git a/app/soapbox/normalizers/soapbox/soapbox-config.ts b/app/soapbox/normalizers/soapbox/soapbox-config.ts index 31e24aa1c..28e6ebb7a 100644 --- a/app/soapbox/normalizers/soapbox/soapbox-config.ts +++ b/app/soapbox/normalizers/soapbox/soapbox-config.ts @@ -120,6 +120,8 @@ export const SoapboxConfigRecord = ImmutableRecord({ * On some platforms this can be too blurry without additional configuration. */ mediaPreview: false, + /** Features overrides. */ + features: ImmutableMap(), }, 'SoapboxConfig'); type SoapboxConfigMap = ImmutableMap;