sforkowany z mirror/soapbox
Porównaj commity
4 Commity
Autor | SHA1 | Data |
---|---|---|
Alex Gleason | 829bbc7f48 | |
Alex Gleason | ef86b27435 | |
Alex Gleason | b84e4403ab | |
Alex Gleason | 7ff488bcf3 |
|
@ -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;
|
|
@ -0,0 +1,56 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import List, { ListItem } from 'soapbox/components/list';
|
||||
import { Column, Toggle } from 'soapbox/components/ui';
|
||||
import { useFeatures } from 'soapbox/hooks';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'features.title', defaultMessage: 'Features' },
|
||||
});
|
||||
|
||||
interface IFeaturesPanel {
|
||||
features: Record<string, boolean>,
|
||||
onChange?: (features: Record<string, boolean>) => void,
|
||||
disabled?: boolean,
|
||||
}
|
||||
|
||||
/** A UI for managing conditional feature flags. */
|
||||
const FeaturesPanel: React.FC<IFeaturesPanel> = ({
|
||||
features: userFeatures,
|
||||
onChange,
|
||||
disabled = false,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const autoFeatures = useFeatures();
|
||||
|
||||
const features = useMemo(() => {
|
||||
return Object.assign({ ...autoFeatures }, { ...userFeatures });
|
||||
}, [userFeatures, autoFeatures]);
|
||||
|
||||
const handleChange = (key: string): React.ChangeEventHandler<HTMLInputElement> => {
|
||||
return (e) => {
|
||||
if (onChange) {
|
||||
onChange({ ...userFeatures, [key]: e.target.checked });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.title)}>
|
||||
<List>
|
||||
{Object.keys(autoFeatures).map(key => (
|
||||
<ListItem label={key}>
|
||||
<Toggle
|
||||
checked={features[key]}
|
||||
onChange={handleChange(key)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeaturesPanel;
|
|
@ -109,6 +109,7 @@ import {
|
|||
ThemeEditor,
|
||||
Quotes,
|
||||
ServiceWorkerInfo,
|
||||
FeaturesEditor,
|
||||
EventInformation,
|
||||
EventDiscussion,
|
||||
Events,
|
||||
|
@ -296,7 +297,8 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
|
|||
<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/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='/developers/apps/create' developerOnly page={DefaultPage} component={CreateApp} content={children} />
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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<string, boolean>(),
|
||||
}, 'SoapboxConfig');
|
||||
|
||||
type SoapboxConfigMap = ImmutableMap<string, any>;
|
||||
|
|
Ładowanie…
Reference in New Issue