kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
FeaturesEditor: allow saving changes
rodzic
ef86b27435
commit
829bbc7f48
|
@ -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<IFeaturesEditor> = () => {
|
||||||
|
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<string, boolean> = useAppSelector(state => {
|
||||||
|
return (state.soapbox.get('features') || ImmutableMap()).toJS();
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateFeatures = async (features: Record<string, boolean>) => {
|
||||||
|
const params = rawConfig.set('features', features).toJS();
|
||||||
|
await dispatch(updateSoapboxConfig(params));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = async(features: Record<string, boolean>) => {
|
||||||
|
setSubmitting(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await dispatch(fetchSoapboxConfig(host));
|
||||||
|
await updateFeatures(features);
|
||||||
|
toast.success(intl.formatMessage(messages.saved));
|
||||||
|
setSubmitting(false);
|
||||||
|
} catch (e) {
|
||||||
|
setSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FeaturesPanel
|
||||||
|
features={userFeatures}
|
||||||
|
onChange={handleChange}
|
||||||
|
disabled={submitting}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FeaturesEditor;
|
|
@ -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<IFeaturesEditor> = () => {
|
|
||||||
const settings = useSettings();
|
|
||||||
const features = (settings.get('features') || ImmutableMap()).toJS() as Record<string, any>;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FeaturesPanel features={features} />
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FeaturesEditor;
|
|
|
@ -12,10 +12,15 @@ const messages = defineMessages({
|
||||||
interface IFeaturesPanel {
|
interface IFeaturesPanel {
|
||||||
features: Record<string, boolean>,
|
features: Record<string, boolean>,
|
||||||
onChange?: (features: Record<string, boolean>) => void,
|
onChange?: (features: Record<string, boolean>) => void,
|
||||||
|
disabled?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A UI for managing conditional feature flags. */
|
/** A UI for managing conditional feature flags. */
|
||||||
const FeaturesPanel: React.FC<IFeaturesPanel> = ({ features: userFeatures, onChange }) => {
|
const FeaturesPanel: React.FC<IFeaturesPanel> = ({
|
||||||
|
features: userFeatures,
|
||||||
|
onChange,
|
||||||
|
disabled = false,
|
||||||
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const autoFeatures = useFeatures();
|
const autoFeatures = useFeatures();
|
||||||
|
|
||||||
|
@ -36,7 +41,11 @@ const FeaturesPanel: React.FC<IFeaturesPanel> = ({ features: userFeatures, onCha
|
||||||
<List>
|
<List>
|
||||||
{Object.keys(autoFeatures).map(key => (
|
{Object.keys(autoFeatures).map(key => (
|
||||||
<ListItem label={key}>
|
<ListItem label={key}>
|
||||||
<Toggle checked={features[key]} onChange={handleChange(key)} />
|
<Toggle
|
||||||
|
checked={features[key]}
|
||||||
|
onChange={handleChange(key)}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
|
|
|
@ -109,7 +109,7 @@ import {
|
||||||
ThemeEditor,
|
ThemeEditor,
|
||||||
Quotes,
|
Quotes,
|
||||||
ServiceWorkerInfo,
|
ServiceWorkerInfo,
|
||||||
DevelopersFeaturesEditor,
|
FeaturesEditor,
|
||||||
EventInformation,
|
EventInformation,
|
||||||
EventDiscussion,
|
EventDiscussion,
|
||||||
Events,
|
Events,
|
||||||
|
@ -297,13 +297,13 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
|
||||||
<WrappedRoute path='/soapbox/admin/reports' staffOnly page={AdminPage} component={Dashboard} content={children} exact />
|
<WrappedRoute path='/soapbox/admin/reports' staffOnly page={AdminPage} component={Dashboard} content={children} exact />
|
||||||
<WrappedRoute path='/soapbox/admin/log' staffOnly page={AdminPage} component={ModerationLog} content={children} exact />
|
<WrappedRoute path='/soapbox/admin/log' staffOnly page={AdminPage} component={ModerationLog} content={children} exact />
|
||||||
<WrappedRoute path='/soapbox/admin/users' staffOnly page={AdminPage} component={UserIndex} content={children} exact />
|
<WrappedRoute path='/soapbox/admin/users' staffOnly page={AdminPage} component={UserIndex} content={children} exact />
|
||||||
<WrappedRoute path='/soapbox/admin/theme' staffOnly page={AdminPage} component={ThemeEditor} content={children} exact />
|
<WrappedRoute path='/soapbox/admin/theme' adminOnly page={AdminPage} component={ThemeEditor} content={children} exact />
|
||||||
|
<WrappedRoute path='/soapbox/admin/features' adminOnly page={AdminPage} component={FeaturesEditor} content={children} />
|
||||||
<WrappedRoute path='/info' page={EmptyPage} component={ServerInfo} content={children} />
|
<WrappedRoute path='/info' page={EmptyPage} component={ServerInfo} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/developers/apps/create' developerOnly page={DefaultPage} component={CreateApp} content={children} />
|
<WrappedRoute path='/developers/apps/create' developerOnly page={DefaultPage} component={CreateApp} content={children} />
|
||||||
<WrappedRoute path='/developers/settings_store' developerOnly page={DefaultPage} component={SettingsStore} content={children} />
|
<WrappedRoute path='/developers/settings_store' developerOnly page={DefaultPage} component={SettingsStore} content={children} />
|
||||||
<WrappedRoute path='/developers/timeline' developerOnly page={DefaultPage} component={TestTimeline} content={children} />
|
<WrappedRoute path='/developers/timeline' developerOnly page={DefaultPage} component={TestTimeline} content={children} />
|
||||||
<WrappedRoute path='/developers/features' developerOnly page={DefaultPage} component={DevelopersFeaturesEditor} content={children} />
|
|
||||||
<WrappedRoute path='/developers/sw' developerOnly page={DefaultPage} component={ServiceWorkerInfo} content={children} />
|
<WrappedRoute path='/developers/sw' developerOnly page={DefaultPage} component={ServiceWorkerInfo} content={children} />
|
||||||
<WrappedRoute path='/developers' page={DefaultPage} component={Developers} content={children} />
|
<WrappedRoute path='/developers' page={DefaultPage} component={Developers} content={children} />
|
||||||
<WrappedRoute path='/error/network' developerOnly page={EmptyPage} component={() => new Promise((_resolve, reject) => reject())} content={children} />
|
<WrappedRoute path='/error/network' developerOnly page={EmptyPage} component={() => new Promise((_resolve, reject) => reject())} content={children} />
|
||||||
|
|
|
@ -310,6 +310,10 @@ export function ModerationLog() {
|
||||||
return import(/* webpackChunkName: "features/admin/moderation_log" */'../../admin/moderation-log');
|
return import(/* webpackChunkName: "features/admin/moderation_log" */'../../admin/moderation-log');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function FeaturesEditor() {
|
||||||
|
return import(/* webpackChunkName: "features/admin" */'../../admin/features-editor');
|
||||||
|
}
|
||||||
|
|
||||||
export function ThemeEditor() {
|
export function ThemeEditor() {
|
||||||
return import(/* webpackChunkName: "features/theme-editor" */'../../theme-editor');
|
return import(/* webpackChunkName: "features/theme-editor" */'../../theme-editor');
|
||||||
}
|
}
|
||||||
|
@ -474,10 +478,6 @@ export function ServiceWorkerInfo() {
|
||||||
return import(/* webpackChunkName: "features/developers" */'../../developers/service-worker-info');
|
return import(/* webpackChunkName: "features/developers" */'../../developers/service-worker-info');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DevelopersFeaturesEditor() {
|
|
||||||
return import(/* webpackChunkName: "features/developers" */'../../developers/features-editor');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DatePicker() {
|
export function DatePicker() {
|
||||||
return import(/* webpackChunkName: "date_picker" */'../../birthdays/date-picker');
|
return import(/* webpackChunkName: "date_picker" */'../../birthdays/date-picker');
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,8 @@ export const SoapboxConfigRecord = ImmutableRecord({
|
||||||
* On some platforms this can be too blurry without additional configuration.
|
* On some platforms this can be too blurry without additional configuration.
|
||||||
*/
|
*/
|
||||||
mediaPreview: false,
|
mediaPreview: false,
|
||||||
|
/** Features overrides. */
|
||||||
|
features: ImmutableMap<string, boolean>(),
|
||||||
}, 'SoapboxConfig');
|
}, 'SoapboxConfig');
|
||||||
|
|
||||||
type SoapboxConfigMap = ImmutableMap<string, any>;
|
type SoapboxConfigMap = ImmutableMap<string, any>;
|
||||||
|
|
Ładowanie…
Reference in New Issue