Porównaj commity

...

4 Commity

Autor SHA1 Wiadomość Data
Alex Gleason 829bbc7f48
FeaturesEditor: allow saving changes 2023-01-13 21:39:21 -06:00
Alex Gleason ef86b27435
FeaturesPanel: add onChange event 2023-01-13 21:10:00 -06:00
Alex Gleason b84e4403ab
Merge remote-tracking branch 'origin/scopes' into features 2023-01-13 20:14:51 -06:00
Alex Gleason 7ff488bcf3
Scaffold features editor 2022-11-17 13:09:04 -06:00
5 zmienionych plików z 125 dodań i 1 usunięć

Wyświetl plik

@ -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;

Wyświetl plik

@ -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;

Wyświetl plik

@ -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} />

Wyświetl plik

@ -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');
}

Wyświetl plik

@ -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>;