mirror of
https://github.com/wassname/talk.git
synced 2026-06-27 17:50:42 +08:00
[CORL-729] Upgrade Final Form & improve tests (#2735)
* CORL-729 Upgrade final form, fix and improve tests This is a squashed rebase from these commits: a300b31c23ab11e5e6f0668bc03ece7697360aaa feat: error on optimisic response warnings during test dd8a9776865ec41d346e23ae0743d0d4fb0caa21 fix: turn off @typescript-eslint/prefer-regexp-exec rule b995daacf1722cace60d755e672cb6a3a20d6bc2 feat: mute false warnings in test e44f9e28307cd63a82c1fb7ac013667dd7b7bc46 fix: wrap remaining tests with act afbd4329b97f3dbc9f873ea4ff234d98bb651ccf feat: fail when act(async () => ...) without await warnings 51dfb60b7d75411ba2e1a28db33f4aba5cf84de1 feat: fail tests with act warnings 97f93546ed8113e207882411eb4cdb7675b0796c fix: mock window.resizeTo globally in tests 850958b8c4d2fc0aca67ae580296433af223f8ab fix: more tests with act 24c05ab88e9a416e4962acc3f20f2c764ba07657 fix: wrap charCountEditComment test in acts ed590b82d147470bba74055dc682e6b70d2e76fb fix signInWithEmail 4a2b9402cb6ce9565d99ae1a950eaa422ff603c3 fix: PostCommentFormContainer 815ebe6ef364d954d4bd0a35495934c9d014170b fix: use final form initialize instead of reset and remove obsolete d3101f2112ed3ffe8d06609620e31e6655d2cf6d Merge branch 'feature/CORL-729-final-form' of https://github.com/coralproject/talk into feature/CORL-729-final-form a0658da610a5f39b6fae78ffb8dd291b22d54e50 fix: addEmailAddress 60f7fc99a44dfa49dfd401a0ef49c60973b3e8e3 fix: use proper act pattern in renderConfigure.spec.tsx d66bdfc2245c2b1ee03a1b3a3a56f1d5ba14ddc4 Stop propagation of Modal content click events (#2706) ec6689594136e22a5b9f05ea284162702dc4955d fix: use proper act pattern in createUsername.spec.tsx ef239496964a5f9a91ee1c4424ad81537c4f47fe fix: stream configuration c7e06a0c6aef6b299c41392af81f8a20855028bf fix: user proper act pattern in streamConfiguration 9712e659e394a898500ed649464ff14d4870e589 Merge branch 'feature/CORL-729-final-form' of https://github.com/coralproject/talk into feature/CORL-729-final-form 9e5cfbaf3593615b457055de23f187fa07edd4c4 fix: signIn test 99b44a4a1bbb7ff2cd44c6821ad33b80f90c4a99 fix: user proper act pattern in stories.spec.tsx ed7c1a92f93ab9aaa85ff92837b0ed21560cb358 fix: user proper act pattern in addEmailAddress a04b392cb2148b9a24791b062027796c409d053e fix: remove obsolete snapshots 59df67c0f9b9d26c74e2cca7d333f5868b0b202d fix: signUp test 7656f179df95b4cd96b37afbc88a1c3a2944fdf4 fix: use proper act pattern on createPassword 85246fbf1f9ab49ad3a09c11ab79bf537059b548 fix: use correct act() pattern in createUsername.spec.tsx d5239373a2d1bbed0bfe8c1ca62ef6a70ef5c7d9 fix: the correct way to use act on form submit :-) d84ecd168354f4acb422a5ddb725fb8faf9c3184 fix: moderation test d8df62ab1a6486144684ff917c47e6e375ffbe03 fix: reportComment test 2756e3184bb292168e8d34e201f340c3799941e6 fix: auth tests a28695dbdd313a7bc3dade9ac1f92d6ef0061526 Properly handle final-form actions in tests 2fafc8ea3458c5b15b66f3d65f0947672dd1a516 Update snapshots now that final form isn't overwriting props 1f9bbaec8678a7653124898ba4a2e84ddc1ef243 fix: prevent final form from overwriting field props f6c66c003d1917db2dcb3f757e8a303266c381fa fix: prevent final form fields overwrite out props 48d1fc7318ee4ba7bf72839127e9a0b1487c1729 chore: rename translation string 728373da5728a4e7c039bd0c3a3cf0037e9f5177 fix: purge relay metadata from request 7cdea925087a6b9b6e318bbb1b31b798be87dc2f fix: radiobuttons 9735bae79222219a81a28d458976a596201b650b chore: revert obsolete checks 0b556e1693584430a5814e81d87b0f233efd1a30 fix: refactor admin configure b245afc7b196035bcb454e031c966e63c77ce522 fix: implement withForm HOC 5787400051211f5d2e1773d7207f32b66b02a2a1 Update the Configure page form state to properly load form values 8c2af3e22a96a3d2e50e7f06fb45d1fb79cf0c8e Replace form.reset() with setTimeout(form.reset) 27d9c90e3f0166cc2db45db461619be15a3cb950 Update radio buttons and on/off fields to work with final-form updates b852dd14af85b14ff8e0d2823e1e83bf278b29b9 Replace any on OnSubmit with typed form submission interfaces f049a70aaf4872825ac3b2aa62dc5cb7f945290a (f) Preliminarily get Coral compiling with latest final-form libraries * feat: act is now actAndReturn * fix: print original filename and line number in custom console impl * feat: trace process warnings * fix: server warnings about potentially memory leak with too many listeners
This commit is contained in:
@@ -102,6 +102,8 @@ let typescriptTypeCheckingOverrides = {
|
||||
},
|
||||
},
|
||||
}],
|
||||
// 28.11.19: (cvle) Disabled because behavior of regexp.exec seems different than str.match?
|
||||
"@typescript-eslint/prefer-regexp-exec": "off",
|
||||
"@typescript-eslint/require-await": "off",
|
||||
"@typescript-eslint/no-misused-promises": "off",
|
||||
"@typescript-eslint/unbound-method": "off", // 10.10.19: (cvle) seems to give false positive.
|
||||
|
||||
@@ -6,6 +6,7 @@ module.exports = {
|
||||
roots: ["<rootDir>/src"],
|
||||
collectCoverageFrom: ["**/*.{js,jsx,mjs,ts,tsx}"],
|
||||
coveragePathIgnorePatterns: ["/node_modules/"],
|
||||
setupFilesAfterEnv: ["<rootDir>/src/core/server/test/setupTestFramework.ts"],
|
||||
testMatch: ["**/*.spec.{js,jsx,mjs,ts,tsx}"],
|
||||
testPathIgnorePatterns: ["/node_modules/", "/client/"],
|
||||
testEnvironment: "node",
|
||||
|
||||
Generated
+38
-8
@@ -4342,6 +4342,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/stack-trace": {
|
||||
"version": "0.0.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz",
|
||||
"integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/stack-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
|
||||
@@ -14549,12 +14555,12 @@
|
||||
}
|
||||
},
|
||||
"final-form": {
|
||||
"version": "4.11.0",
|
||||
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.11.0.tgz",
|
||||
"integrity": "sha512-uO0VhAEbWvV8LHBpBHoIpbYqonYBXyd8gpTYFEAPCHEy5tcNqdVX4EnWQoxKl//+U9Vk0kTUsnPOWiBP2PiOUg==",
|
||||
"version": "4.18.6",
|
||||
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.18.6.tgz",
|
||||
"integrity": "sha512-LssWTXTGkoNv5WxowFTTUyC86oLYlEHH2glARBxOq8RpjiQdOyUyVG7qfPaCeOwoOw7Y5wrhSWdePyOydqSrCw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.5"
|
||||
"@babel/runtime": "^7.3.1"
|
||||
}
|
||||
},
|
||||
"finalhandler": {
|
||||
@@ -29389,12 +29395,30 @@
|
||||
"dev": true
|
||||
},
|
||||
"react-final-form": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-4.0.2.tgz",
|
||||
"integrity": "sha512-EdFWrT8nMWu5sHViuZ/VmlaYT+mLu/q5TMDWZZj5gnssUAWfgcila0QpptFNDg6+qNtepGgFh5ZPASlivoEXUA==",
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.3.0.tgz",
|
||||
"integrity": "sha512-jijhXR1fFGUBQwNOSqF4MK8XJO7Ynl1p8vcFsnQS0INSkGI52+4IagjUgtHj3w8EviIHPFK/Eflji6FELUl07w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2"
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"ts-essentials": "^2.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz",
|
||||
"integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.2"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-helmet": {
|
||||
@@ -33398,6 +33422,12 @@
|
||||
"integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==",
|
||||
"dev": true
|
||||
},
|
||||
"ts-essentials": {
|
||||
"version": "2.0.12",
|
||||
"resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-2.0.12.tgz",
|
||||
"integrity": "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==",
|
||||
"dev": true
|
||||
},
|
||||
"ts-invariant": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz",
|
||||
|
||||
+7
-5
@@ -48,7 +48,7 @@
|
||||
"lint:scripts": "eslint 'scripts/**/*.{js,ts,tsx}'",
|
||||
"lint:graphql": "graphql-schema-linter src/core/server/graph/tenant/schema/schema.graphql",
|
||||
"lint-fix": "npm run lint:server -- --fix && npm run lint:client -- --fix && npm run lint:scripts -- --fix",
|
||||
"test": "node scripts/test.js --env=jsdom",
|
||||
"test": "node --trace-warnings scripts/test.js --env=jsdom",
|
||||
"tscheck": "npm-run-all --parallel tscheck:*",
|
||||
"tscheck:server": "tsc --project ./src/tsconfig.json --noEmit",
|
||||
"tscheck:client": "tsc --project ./src/core/client/tsconfig.json --noEmit",
|
||||
@@ -223,6 +223,7 @@
|
||||
"@types/simplemde": "^1.11.7",
|
||||
"@types/sinon": "^7.0.11",
|
||||
"@types/source-map-support": "^0.5.0",
|
||||
"@types/stack-trace": "0.0.29",
|
||||
"@types/stack-utils": "^1.0.1",
|
||||
"@types/throng": "^4.0.2",
|
||||
"@types/uuid": "^3.4.4",
|
||||
@@ -275,7 +276,7 @@
|
||||
"eslint-plugin-react": "^7.15.1",
|
||||
"eventemitter2": "^5.0.1",
|
||||
"farce": "^0.2.6",
|
||||
"final-form": "4.11.0",
|
||||
"final-form": "4.18.6",
|
||||
"flat": "^4.1.0",
|
||||
"fluent-intl-polyfill": "^0.1.0",
|
||||
"fluent-langneg": "^0.1.1",
|
||||
@@ -326,7 +327,7 @@
|
||||
"react-dev-utils": "^9.0.0",
|
||||
"react-dom": "^16.9.0-alpha.0",
|
||||
"react-error-overlay": "^5.1.6",
|
||||
"react-final-form": "4.0.2",
|
||||
"react-final-form": "6.3.0",
|
||||
"react-popper": "^1.3.2",
|
||||
"react-relay": "^4.0.0",
|
||||
"react-relay-network-modern": "^4.0.4",
|
||||
@@ -347,6 +348,7 @@
|
||||
"simulant": "^0.2.2",
|
||||
"sinon": "^7.3.2",
|
||||
"sockjs-client": "^1.3.0",
|
||||
"stack-trace": "0.0.10",
|
||||
"strip-ansi": "^5.2.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"subscriptions-transport-ws": "^0.9.16",
|
||||
@@ -362,11 +364,11 @@
|
||||
"tslint": "^5.20.0",
|
||||
"typed-css-modules": "^0.4.2",
|
||||
"typeface-manuale": "^0.0.71",
|
||||
"typeface-nunito": "0.0.72",
|
||||
"typeface-open-sans": "0.0.75",
|
||||
"typeface-source-sans-pro": "^0.0.54",
|
||||
"typescript": "3.3.4000",
|
||||
"typescript-snapshots-plugin": "^1.6.0",
|
||||
"typeface-nunito": "0.0.72",
|
||||
"typeface-open-sans": "0.0.75",
|
||||
"wait-for-expect": "^1.1.1",
|
||||
"webpack": "^4.30.0",
|
||||
"webpack-assets-manifest": "^3.1.1",
|
||||
|
||||
@@ -52,7 +52,7 @@ const UserDrawerNotesContainer: FunctionComponent<Props> = ({
|
||||
userID: user.id,
|
||||
body,
|
||||
});
|
||||
form.reset();
|
||||
form.initialize({});
|
||||
},
|
||||
[user]
|
||||
);
|
||||
|
||||
@@ -25,6 +25,11 @@ interface Props {
|
||||
lastFocusableRef: RefObject<any>;
|
||||
}
|
||||
|
||||
interface FormStateValues {
|
||||
duration: any;
|
||||
emailMessage: any;
|
||||
}
|
||||
|
||||
const DURATIONS: ScaledUnit[] = [
|
||||
{ original: 3600, value: "3600", unit: "hour", scaled: 1 }, // 1 hour
|
||||
{ original: 10800, value: "10800", unit: "hour", scaled: 3 }, // 3 hours
|
||||
@@ -69,7 +74,8 @@ const SuspendForm: FunctionComponent<Props> = ({
|
||||
const setMessageValue: Mutator = useCallback(
|
||||
([name, newValue], state, { changeValue }) => {
|
||||
if (state.lastFormState) {
|
||||
const { duration, emailMessage } = state.lastFormState.values;
|
||||
const { duration, emailMessage } = state.lastFormState
|
||||
.values as FormStateValues;
|
||||
const unit = DURATIONS.find(d => d.value === duration);
|
||||
const expectedEmailMessage = getMessageWithDuration(unit!);
|
||||
if (expectedEmailMessage === emailMessage) {
|
||||
@@ -86,7 +92,8 @@ const SuspendForm: FunctionComponent<Props> = ({
|
||||
{ changeValue }
|
||||
) => {
|
||||
if (state.lastFormState && !checked) {
|
||||
const { duration, emailMessage } = state.lastFormState.values;
|
||||
const { duration, emailMessage } = state.lastFormState
|
||||
.values as FormStateValues;
|
||||
const unit = DURATIONS.find(d => d.value === duration);
|
||||
const expectedEmailMessage = getMessageWithDuration(unit!);
|
||||
if (expectedEmailMessage !== emailMessage) {
|
||||
@@ -132,8 +139,8 @@ const SuspendForm: FunctionComponent<Props> = ({
|
||||
$unit={unit}
|
||||
>
|
||||
<RadioButton
|
||||
id={`duration-${value}`}
|
||||
{...input}
|
||||
id={`duration-${value}`}
|
||||
onChange={event => {
|
||||
form.mutators.setMessageValue(
|
||||
"emailMessage",
|
||||
@@ -156,8 +163,8 @@ const SuspendForm: FunctionComponent<Props> = ({
|
||||
{({ input }) => (
|
||||
<Localized id="community-suspendModal-customize">
|
||||
<CheckBox
|
||||
id="suspendModal-editMessage"
|
||||
{...input}
|
||||
id="suspendModal-editMessage"
|
||||
onChange={event => {
|
||||
form.mutators.resetMessageValue(
|
||||
"emailMessage",
|
||||
|
||||
@@ -20,11 +20,11 @@ const EmailField: FunctionComponent<Props> = ({ index, disabled }) => (
|
||||
<Label htmlFor={input.name}>Email Address:</Label>
|
||||
</Localized>
|
||||
<TextField
|
||||
{...input}
|
||||
data-testid={`invite-users-email.${index}`}
|
||||
color={colorFromMeta(meta)}
|
||||
disabled={disabled}
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
<ValidationMessage meta={meta} fullWidth />
|
||||
</FormField>
|
||||
|
||||
@@ -21,9 +21,9 @@ const RoleField: FunctionComponent<Props> = ({ disabled }) => (
|
||||
{({ input }) => (
|
||||
<Localized id="role-staff">
|
||||
<RadioButton
|
||||
{...input}
|
||||
id={`${input.name}-staff`}
|
||||
disabled={disabled}
|
||||
{...input}
|
||||
>
|
||||
Staff
|
||||
</RadioButton>
|
||||
@@ -34,9 +34,9 @@ const RoleField: FunctionComponent<Props> = ({ disabled }) => (
|
||||
{({ input }) => (
|
||||
<Localized id="role-moderator">
|
||||
<RadioButton
|
||||
{...input}
|
||||
id={`${input.name}-moderator`}
|
||||
disabled={disabled}
|
||||
{...input}
|
||||
>
|
||||
Moderator
|
||||
</RadioButton>
|
||||
@@ -47,9 +47,9 @@ const RoleField: FunctionComponent<Props> = ({ disabled }) => (
|
||||
{({ input }) => (
|
||||
<Localized id="role-admin">
|
||||
<RadioButton
|
||||
{...input}
|
||||
id={`${input.name}-admin`}
|
||||
disabled={disabled}
|
||||
{...input}
|
||||
>
|
||||
Admin
|
||||
</RadioButton>
|
||||
|
||||
@@ -60,12 +60,12 @@ const UserTableFilter: FunctionComponent<Props> = props => (
|
||||
attrs={{ placeholder: true, "aria-label": true }}
|
||||
>
|
||||
<TextField
|
||||
{...input}
|
||||
className={styles.textField}
|
||||
color="dark"
|
||||
placeholder="Search by username or email address..."
|
||||
aria-label="Search by username or email address"
|
||||
variant="seamlessAdornment"
|
||||
{...input}
|
||||
adornment={
|
||||
<Localized
|
||||
id="community-filter-searchButton"
|
||||
|
||||
@@ -14,7 +14,7 @@ import SideBar from "./SideBar";
|
||||
|
||||
interface Props {
|
||||
onSubmit: (settings: any, form: FormApi) => void;
|
||||
onChange: (formState: FormState) => void;
|
||||
onChange: (formState: FormState<any>) => void;
|
||||
children: React.ReactElement;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ const Configure: FunctionComponent<Props> = ({
|
||||
}) => (
|
||||
<MainLayout data-testid="configure-container">
|
||||
<Form onSubmit={onSubmit}>
|
||||
{({ handleSubmit, submitting, pristine, form, submitError }) => (
|
||||
{({ handleSubmit, submitting, form, pristine, submitError }) => (
|
||||
<form autoComplete="off" onSubmit={handleSubmit} id="configure-form">
|
||||
<FormSpy onChange={onChange} />
|
||||
<Layout>
|
||||
|
||||
@@ -36,7 +36,7 @@ class ConfigureRoute extends React.Component<Props, State> {
|
||||
form.initialize(data);
|
||||
};
|
||||
|
||||
private handleChange = ({ dirty }: FormState) => {
|
||||
private handleChange = ({ dirty }: FormState<any>) => {
|
||||
if (dirty !== this.state.dirty) {
|
||||
this.setState({ dirty });
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
|
||||
import { parseStringBool } from "coral-framework/lib/form";
|
||||
import { formatBool, parseStringBool } from "coral-framework/lib/form";
|
||||
import { Validator } from "coral-framework/lib/validation";
|
||||
import { RadioButton } from "coral-ui/components/v2";
|
||||
|
||||
@@ -13,8 +13,8 @@ interface Props {
|
||||
invert?: boolean;
|
||||
onLabel?: React.ReactNode;
|
||||
offLabel?: React.ReactNode;
|
||||
format?: ((value: any, name: string) => any) | null;
|
||||
parse?: ((value: any, name: string) => any) | null;
|
||||
format?: (value: any, name: string) => any;
|
||||
parse?: (value: any, name: string) => any;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -25,19 +25,19 @@ const OnOffField: FunctionComponent<Props> = ({
|
||||
offLabel,
|
||||
invert = false,
|
||||
parse = parseStringBool,
|
||||
format,
|
||||
format = formatBool,
|
||||
className,
|
||||
}) => (
|
||||
<div className={className}>
|
||||
<Field
|
||||
name={name}
|
||||
type="radio"
|
||||
value={!invert}
|
||||
value={JSON.stringify(!invert)}
|
||||
parse={parse}
|
||||
format={format}
|
||||
>
|
||||
{({ input }) => (
|
||||
<RadioButton id={`${input.name}-true`} disabled={disabled} {...input}>
|
||||
<RadioButton {...input} id={`${input.name}-true`} disabled={disabled}>
|
||||
{onLabel || (
|
||||
<Localized id="configure-onOffField-on">
|
||||
<span>On</span>
|
||||
@@ -51,10 +51,10 @@ const OnOffField: FunctionComponent<Props> = ({
|
||||
type="radio"
|
||||
parse={parse}
|
||||
format={format}
|
||||
value={invert}
|
||||
value={JSON.stringify(invert)}
|
||||
>
|
||||
{({ input }) => (
|
||||
<RadioButton id={`${input.name}-false`} disabled={disabled} {...input}>
|
||||
<RadioButton {...input} id={`${input.name}-false`} disabled={disabled}>
|
||||
{offLabel || (
|
||||
<Localized id="configure-onOffField-off">
|
||||
<span>Off</span>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
|
||||
import { parseStringBool } from "coral-framework/lib/form";
|
||||
import { Validator } from "coral-framework/lib/validation";
|
||||
import { RadioButton } from "coral-ui/components/v2";
|
||||
|
||||
interface Props {
|
||||
validate?: Validator;
|
||||
name: string;
|
||||
disabled: boolean;
|
||||
invert?: boolean;
|
||||
}
|
||||
|
||||
const PermissionField: FunctionComponent<Props> = ({
|
||||
name,
|
||||
disabled,
|
||||
invert = false,
|
||||
}) => (
|
||||
<div>
|
||||
<Field name={name} type="radio" parse={parseStringBool} value={!invert}>
|
||||
{({ input }) => (
|
||||
<Localized id="configure-permissionField-allow">
|
||||
<RadioButton
|
||||
id={`${input.name}-allow`}
|
||||
disabled={disabled}
|
||||
{...input}
|
||||
>
|
||||
Allow
|
||||
</RadioButton>
|
||||
</Localized>
|
||||
)}
|
||||
</Field>
|
||||
<Field name={name} type="radio" parse={parseStringBool} value={invert}>
|
||||
{({ input }) => (
|
||||
<Localized id="configure-permissionField-dontAllow">
|
||||
<RadioButton
|
||||
id={`${input.name}-dontAllow`}
|
||||
disabled={disabled}
|
||||
{...input}
|
||||
>
|
||||
Don't allow
|
||||
</RadioButton>
|
||||
</Localized>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default PermissionField;
|
||||
@@ -1,54 +0,0 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import CommentStreamLiveUpdatesContainer from "./CommentStreamLiveUpdatesContainer";
|
||||
import CustomCSSConfigContainer from "./CustomCSSConfigContainer";
|
||||
import EmbedCodeContainer from "./EmbedCodeContainer";
|
||||
import PermittedDomainsConfigContainer from "./PermittedDomainsConfigContainer";
|
||||
import StoryCreationConfigContainer from "./StoryCreationConfigContainer";
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
settings: PropTypesOf<typeof CustomCSSConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof PermittedDomainsConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof CommentStreamLiveUpdatesContainer>["settings"] &
|
||||
PropTypesOf<typeof CommentStreamLiveUpdatesContainer>["settingsReadOnly"] &
|
||||
PropTypesOf<typeof EmbedCodeContainer>["settings"] &
|
||||
PropTypesOf<typeof StoryCreationConfigContainer>["settings"];
|
||||
onInitValues: (values: any) => void;
|
||||
}
|
||||
|
||||
const AdvancedConfig: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
settings,
|
||||
onInitValues,
|
||||
}) => (
|
||||
<HorizontalGutter size="double" data-testid="configure-advancedContainer">
|
||||
<EmbedCodeContainer settings={settings} />
|
||||
<CustomCSSConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<CommentStreamLiveUpdatesContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
settingsReadOnly={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<PermittedDomainsConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<StoryCreationConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
export default AdvancedConfig;
|
||||
+35
-36
@@ -1,57 +1,56 @@
|
||||
import { FormApi } from "final-form";
|
||||
import { RouteProps } from "found";
|
||||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { useForm } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { pureMerge } from "coral-common/utils";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import {
|
||||
purgeMetadata,
|
||||
withFragmentContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { HorizontalGutter } from "coral-ui/components";
|
||||
|
||||
import { AdvancedConfigContainer_settings } from "coral-admin/__generated__/AdvancedConfigContainer_settings.graphql";
|
||||
|
||||
import AdvancedConfig from "./AdvancedConfig";
|
||||
import CommentStreamLiveUpdatesContainer from "./CommentStreamLiveUpdatesContainer";
|
||||
import CustomCSSConfig from "./CustomCSSConfig";
|
||||
import EmbedCodeContainer from "./EmbedCodeContainer";
|
||||
import PermittedDomainsConfig from "./PermittedDomainsConfig";
|
||||
import StoryCreationConfig from "./StoryCreationConfig";
|
||||
|
||||
interface Props {
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
settings: AdvancedConfigContainer_settings;
|
||||
}
|
||||
|
||||
class AdvancedConfigContainer extends React.Component<Props> {
|
||||
public static routeConfig: RouteProps;
|
||||
private initialValues = {};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.props.form.initialize(this.initialValues);
|
||||
}
|
||||
|
||||
private handleOnInitValues = (values: any) => {
|
||||
this.initialValues = pureMerge(this.initialValues, values);
|
||||
};
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<AdvancedConfig
|
||||
disabled={this.props.submitting}
|
||||
settings={this.props.settings}
|
||||
onInitValues={this.handleOnInitValues}
|
||||
const AdvancedConfigContainer: React.FunctionComponent<Props> = ({
|
||||
settings,
|
||||
submitting,
|
||||
}) => {
|
||||
const form = useForm();
|
||||
useMemo(() => form.initialize(purgeMetadata(settings)), []);
|
||||
return (
|
||||
<HorizontalGutter size="double" data-testid="configure-advancedContainer">
|
||||
<EmbedCodeContainer settings={settings} />
|
||||
<CustomCSSConfig disabled={submitting} />
|
||||
<CommentStreamLiveUpdatesContainer
|
||||
disabled={submitting}
|
||||
settings={settings}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
<PermittedDomainsConfig disabled={submitting} />
|
||||
<StoryCreationConfig disabled={submitting} />
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment AdvancedConfigContainer_settings on Settings {
|
||||
...CustomCSSConfig_formValues @relay(mask: false)
|
||||
...PermittedDomainsConfig_formValues @relay(mask: false)
|
||||
...CommentStreamLiveUpdates_formValues @relay(mask: false)
|
||||
...StoryCreationConfig_formValues @relay(mask: false)
|
||||
|
||||
...EmbedCodeContainer_settings
|
||||
...CustomCSSConfigContainer_settings
|
||||
...PermittedDomainsConfigContainer_settings
|
||||
...CommentStreamLiveUpdatesContainer_settings
|
||||
...CommentStreamLiveUpdatesContainer_settingsReadOnly
|
||||
...StoryCreationConfigContainer_settings
|
||||
}
|
||||
`,
|
||||
})(AdvancedConfigContainer);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { FormApi } from "final-form";
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
@@ -11,7 +10,6 @@ import AdvancedConfigContainer from "./AdvancedConfigContainer";
|
||||
|
||||
interface Props {
|
||||
data: AdvancedConfigRouteQueryResponse | null;
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
}
|
||||
|
||||
@@ -27,7 +25,6 @@ class AdvancedConfigRoute extends React.Component<Props> {
|
||||
return (
|
||||
<AdvancedConfigContainer
|
||||
settings={this.props.data.settings}
|
||||
form={this.props.form}
|
||||
submitting={this.props.submitting}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { FormField, FormFieldDescription } from "coral-ui/components/v2";
|
||||
|
||||
@@ -7,6 +8,15 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import OnOffField from "../../OnOffField";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment CommentStreamLiveUpdates_formValues on Settings {
|
||||
live {
|
||||
enabled
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
+10
-29
@@ -4,49 +4,30 @@ import { graphql } from "react-relay";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { CommentStreamLiveUpdatesContainer_settings } from "coral-admin/__generated__/CommentStreamLiveUpdatesContainer_settings.graphql";
|
||||
import { CommentStreamLiveUpdatesContainer_settingsReadOnly } from "coral-admin/__generated__/CommentStreamLiveUpdatesContainer_settingsReadOnly.graphql";
|
||||
|
||||
import CommentStreamLiveUpdates from "./CommentStreamLiveUpdates";
|
||||
|
||||
interface Props {
|
||||
settingsReadOnly: CommentStreamLiveUpdatesContainer_settingsReadOnly;
|
||||
settings: CommentStreamLiveUpdatesContainer_settings;
|
||||
onInitValues: (values: CommentStreamLiveUpdatesContainer_settings) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class CommentStreamLiveUpdatesContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
const CommentStreamLiveUpdatesContainer: React.FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
settings: {
|
||||
live: { configurable },
|
||||
},
|
||||
}) => {
|
||||
if (!configurable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
disabled,
|
||||
settingsReadOnly: {
|
||||
live: { configurable },
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
if (!configurable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <CommentStreamLiveUpdates disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
return <CommentStreamLiveUpdates disabled={disabled} />;
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment CommentStreamLiveUpdatesContainer_settings on Settings {
|
||||
live {
|
||||
enabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
settingsReadOnly: graphql`
|
||||
fragment CommentStreamLiveUpdatesContainer_settingsReadOnly on Settings {
|
||||
live {
|
||||
configurable
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { formatEmpty, parseEmptyAsNull } from "coral-framework/lib/form";
|
||||
import { FormField, FormFieldDescription } from "coral-ui/components/v2";
|
||||
@@ -9,6 +10,13 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment CustomCSSConfig_formValues on Settings {
|
||||
customCSSURL
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -34,6 +42,7 @@ const CustomCSSConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
<Field name="customCSSURL" parse={parseEmptyAsNull} format={formatEmpty}>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={`configure-advanced-${input.name}`}
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
@@ -42,7 +51,6 @@ const CustomCSSConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { CustomCSSConfigContainer_settings as SettingsData } from "coral-admin/__generated__/CustomCSSConfigContainer_settings.graphql";
|
||||
|
||||
import CustomCSSConfig from "./CustomCSSConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class CustomCSSConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <CustomCSSConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment CustomCSSConfigContainer_settings on Settings {
|
||||
customCSSURL
|
||||
}
|
||||
`,
|
||||
})(CustomCSSConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { formatStringList, parseStringList } from "coral-framework/lib/form";
|
||||
import { validateStrictURLList } from "coral-framework/lib/validation";
|
||||
@@ -10,6 +11,13 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment PermittedDomainsConfig_formValues on Settings {
|
||||
allowedDomains
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -44,6 +52,7 @@ const PermittedDomainsConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={`configure-advanced-${input.name}`}
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
@@ -51,7 +60,6 @@ const PermittedDomainsConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
autoCapitalize="off"
|
||||
spellCheck={false}
|
||||
meta={meta}
|
||||
{...input}
|
||||
fullWidth
|
||||
/>
|
||||
)}
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { PermittedDomainsConfigContainer_settings as SettingsData } from "coral-admin/__generated__/PermittedDomainsConfigContainer_settings.graphql";
|
||||
|
||||
import PermittedDomainsConfig from "./PermittedDomainsConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class PermittedDomainsConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <PermittedDomainsConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment PermittedDomainsConfigContainer_settings on Settings {
|
||||
allowedDomains
|
||||
}
|
||||
`,
|
||||
})(PermittedDomainsConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { parseEmptyAsNull } from "coral-framework/lib/form";
|
||||
import { ExternalLink } from "coral-framework/lib/i18n/components";
|
||||
@@ -18,6 +19,19 @@ import HelperText from "../../HelperText";
|
||||
import OnOffField from "../../OnOffField";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment StoryCreationConfig_formValues on Settings {
|
||||
stories {
|
||||
scraping {
|
||||
enabled
|
||||
proxyURL
|
||||
}
|
||||
disableLazy
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -90,6 +104,7 @@ const StoryCreationConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id="configure-advanced-stories-proxy-url"
|
||||
disabled={disabled}
|
||||
fullWidth
|
||||
@@ -98,7 +113,6 @@ const StoryCreationConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
autoCapitalize="off"
|
||||
spellCheck={false}
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { StoryCreationConfigContainer_settings as SettingsData } from "coral-admin/__generated__/StoryCreationConfigContainer_settings.graphql";
|
||||
|
||||
import StoryCreationConfig from "./StoryCreationConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class StoryCreationConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <StoryCreationConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment StoryCreationConfigContainer_settings on Settings {
|
||||
stories {
|
||||
scraping {
|
||||
enabled
|
||||
proxyURL
|
||||
}
|
||||
disableLazy
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(StoryCreationConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
Flex,
|
||||
@@ -16,6 +17,17 @@ import OnOffField from "../../OnOffField";
|
||||
|
||||
import styles from "./AccountFeaturesConfig.css";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment AccountFeaturesConfig_formValues on Settings {
|
||||
accountFeatures {
|
||||
changeUsername
|
||||
deleteAccount
|
||||
downloadComments
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { DeepNullable, DeepPartial } from "coral-common/types";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import { GQLSettings } from "coral-framework/schema";
|
||||
|
||||
import { AccountFeaturesConfigContainer_settings as SettingsData } from "coral-admin/__generated__/AccountFeaturesConfigContainer_settings.graphql";
|
||||
|
||||
import AccountFeaturesConfig from "./AccountFeaturesConfig";
|
||||
|
||||
export type FormProps = DeepNullable<Pick<GQLSettings, "accountFeatures">>;
|
||||
export type OnInitValuesFct = (values: DeepPartial<FormProps>) => void;
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
class AccountFeaturesConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <AccountFeaturesConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment AccountFeaturesConfigContainer_settings on Settings {
|
||||
accountFeatures {
|
||||
changeUsername
|
||||
deleteAccount
|
||||
downloadComments
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(AccountFeaturesConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,36 +0,0 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import AuthIntegrationsConfig from "./AuthIntegrationsConfig";
|
||||
import SessionConfigContainer from "./SessionConfigContainer";
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
auth: PropTypesOf<typeof AuthIntegrationsConfig>["auth"] &
|
||||
PropTypesOf<typeof SessionConfigContainer>["auth"];
|
||||
onInitValues: OnInitValuesFct;
|
||||
}
|
||||
|
||||
const AuthConfig: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
auth,
|
||||
onInitValues,
|
||||
}) => (
|
||||
<HorizontalGutter size="double" data-testid="configure-authContainer">
|
||||
<SessionConfigContainer
|
||||
auth={auth}
|
||||
disabled={disabled}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<AuthIntegrationsConfig
|
||||
disabled={disabled}
|
||||
auth={auth}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
export default AuthConfig;
|
||||
@@ -5,24 +5,28 @@ import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { DeepNullable, DeepPartial } from "coral-common/types";
|
||||
import { pureMerge } from "coral-common/utils";
|
||||
import { CoralContext, withContext } from "coral-framework/lib/bootstrap";
|
||||
import {
|
||||
AddSubmitHook,
|
||||
RemoveSubmitHook,
|
||||
SubmitHook,
|
||||
withForm,
|
||||
withSubmitHookContext,
|
||||
} from "coral-framework/lib/form";
|
||||
import { getMessage } from "coral-framework/lib/i18n";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import {
|
||||
purgeMetadata,
|
||||
withFragmentContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { GQLSettings } from "coral-framework/schema";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import { AuthConfigContainer_auth as AuthData } from "coral-admin/__generated__/AuthConfigContainer_auth.graphql";
|
||||
import { AuthConfigContainer_settings as SettingsData } from "coral-admin/__generated__/AuthConfigContainer_settings.graphql";
|
||||
|
||||
import AccountFeaturesConfigContainer from "./AccountFeaturesConfigContainer";
|
||||
import AuthConfig from "./AuthConfig";
|
||||
import AccountFeaturesConfig from "./AccountFeaturesConfig";
|
||||
import AuthIntegrationsConfig from "./AuthIntegrationsConfig";
|
||||
import SessionConfig from "./SessionConfig";
|
||||
|
||||
export type FormProps = DeepNullable<
|
||||
Pick<GQLSettings, "auth" | "accountFeatures">
|
||||
@@ -40,16 +44,17 @@ interface Props {
|
||||
|
||||
class AuthConfigContainer extends React.Component<Props> {
|
||||
public static routeConfig: RouteProps;
|
||||
private initialValues: DeepPartial<FormProps> = {};
|
||||
private removeSubmitHook: RemoveSubmitHook;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.removeSubmitHook = this.props.addSubmitHook(this.submitHook);
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.props.form.initialize(this.initialValues);
|
||||
this.props.form.initialize(
|
||||
purgeMetadata({
|
||||
...props.settings,
|
||||
auth: props.auth,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
@@ -97,51 +102,48 @@ class AuthConfigContainer extends React.Component<Props> {
|
||||
return;
|
||||
};
|
||||
|
||||
private handleOnInitValues: OnInitValuesFct = values => {
|
||||
this.initialValues = pureMerge(this.initialValues, values);
|
||||
};
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<AccountFeaturesConfigContainer
|
||||
onInitValues={this.handleOnInitValues}
|
||||
settings={this.props.settings}
|
||||
disabled={this.props.submitting}
|
||||
/>
|
||||
<AuthConfig
|
||||
disabled={this.props.submitting}
|
||||
<HorizontalGutter size="double" data-testid="configure-authContainer">
|
||||
<AccountFeaturesConfig disabled={this.props.submitting} />
|
||||
<SessionConfig disabled={this.props.submitting} />
|
||||
<AuthIntegrationsConfig
|
||||
auth={this.props.auth}
|
||||
onInitValues={this.handleOnInitValues}
|
||||
disabled={this.props.submitting}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment AuthConfigContainer_settings on Settings {
|
||||
...AccountFeaturesConfigContainer_settings
|
||||
}
|
||||
`,
|
||||
auth: graphql`
|
||||
fragment AuthConfigContainer_auth on Auth {
|
||||
...FacebookConfigContainer_auth
|
||||
...FacebookConfigContainer_authReadOnly
|
||||
...GoogleConfigContainer_auth
|
||||
...GoogleConfigContainer_authReadOnly
|
||||
...SSOConfigContainer_auth
|
||||
...SSOConfigContainer_authReadOnly
|
||||
...LocalAuthConfigContainer_auth
|
||||
...OIDCConfigContainer_auth
|
||||
...OIDCConfigContainer_authReadOnly
|
||||
...SessionConfigContainer_auth
|
||||
}
|
||||
`,
|
||||
})(
|
||||
withSubmitHookContext(addSubmitHook => ({ addSubmitHook }))(
|
||||
withContext(({ localeBundles }) => ({ localeBundles }))(AuthConfigContainer)
|
||||
const enhanced = withForm(
|
||||
withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment AuthConfigContainer_settings on Settings {
|
||||
...AccountFeaturesConfig_formValues @relay(mask: false)
|
||||
}
|
||||
`,
|
||||
auth: graphql`
|
||||
fragment AuthConfigContainer_auth on Auth {
|
||||
...FacebookConfig_formValues @relay(mask: false)
|
||||
...GoogleConfig_formValues @relay(mask: false)
|
||||
...SSOConfig_formValues @relay(mask: false)
|
||||
...LocalAuthConfig_formValues @relay(mask: false)
|
||||
...OIDCConfig_formValues @relay(mask: false)
|
||||
...SessionConfig_formValues @relay(mask: false)
|
||||
|
||||
...FacebookConfigContainer_auth
|
||||
...GoogleConfigContainer_auth
|
||||
...SSOConfigContainer_auth
|
||||
...OIDCConfigContainer_auth
|
||||
}
|
||||
`,
|
||||
})(
|
||||
withSubmitHookContext(addSubmitHook => ({ addSubmitHook }))(
|
||||
withContext(({ localeBundles }) => ({ localeBundles }))(
|
||||
AuthConfigContainer
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { FormApi } from "final-form";
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
@@ -11,7 +10,6 @@ import AuthConfigContainer from "./AuthConfigContainer";
|
||||
|
||||
interface Props {
|
||||
data: AuthConfigRouteContainerQueryResponse | null;
|
||||
form: FormApi;
|
||||
submitting?: boolean;
|
||||
}
|
||||
|
||||
@@ -28,7 +26,6 @@ class AuthConfigRoute extends React.Component<Props> {
|
||||
<AuthConfigContainer
|
||||
settings={this.props.data.settings}
|
||||
auth={this.props.data.settings.auth}
|
||||
form={this.props.form}
|
||||
submitting={this.props.submitting}
|
||||
/>
|
||||
);
|
||||
@@ -42,7 +39,6 @@ const enhanced = withRouteConfig<Props>({
|
||||
...AuthConfigContainer_settings
|
||||
auth {
|
||||
...AuthConfigContainer_auth
|
||||
...SessionConfigContainer_auth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,62 +3,30 @@ import React, { FunctionComponent } from "react";
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import FacebookConfigContainer from "./FacebookConfigContainer";
|
||||
import GoogleConfigContainer from "./GoogleConfigContainer";
|
||||
import LocalAuthConfigContainer from "./LocalAuthConfigContainer";
|
||||
import LocalAuthConfig from "./LocalAuthConfig";
|
||||
import OIDCConfigContainer from "./OIDCConfigContainer";
|
||||
import SSOConfigContainer from "./SSOConfigContainer";
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
auth: PropTypesOf<typeof FacebookConfigContainer>["auth"] &
|
||||
PropTypesOf<typeof FacebookConfigContainer>["authReadOnly"] &
|
||||
PropTypesOf<typeof GoogleConfigContainer>["auth"] &
|
||||
PropTypesOf<typeof GoogleConfigContainer>["authReadOnly"] &
|
||||
PropTypesOf<typeof SSOConfigContainer>["auth"] &
|
||||
PropTypesOf<typeof SSOConfigContainer>["authReadOnly"] &
|
||||
PropTypesOf<typeof LocalAuthConfigContainer>["auth"] &
|
||||
PropTypesOf<typeof OIDCConfigContainer>["auth"] &
|
||||
PropTypesOf<typeof OIDCConfigContainer>["authReadOnly"];
|
||||
onInitValues: OnInitValuesFct;
|
||||
PropTypesOf<typeof OIDCConfigContainer>["auth"];
|
||||
}
|
||||
|
||||
const AuthIntegrationsConfig: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
auth,
|
||||
onInitValues,
|
||||
}) => (
|
||||
<HorizontalGutter size="double">
|
||||
<LocalAuthConfigContainer
|
||||
disabled={disabled}
|
||||
auth={auth}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<OIDCConfigContainer
|
||||
disabled={disabled}
|
||||
auth={auth}
|
||||
authReadOnly={auth}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<SSOConfigContainer
|
||||
disabled={disabled}
|
||||
auth={auth}
|
||||
authReadOnly={auth}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<GoogleConfigContainer
|
||||
disabled={disabled}
|
||||
auth={auth}
|
||||
authReadOnly={auth}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<FacebookConfigContainer
|
||||
disabled={disabled}
|
||||
auth={auth}
|
||||
authReadOnly={auth}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<LocalAuthConfig disabled={disabled} />
|
||||
<OIDCConfigContainer disabled={disabled} auth={auth} />
|
||||
<SSOConfigContainer disabled={disabled} auth={auth} />
|
||||
<GoogleConfigContainer disabled={disabled} auth={auth} />
|
||||
<FacebookConfigContainer disabled={disabled} auth={auth} />
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
|
||||
@@ -20,21 +20,24 @@ const ClientSecretField: FunctionComponent<Props> = ({
|
||||
validate,
|
||||
}) => (
|
||||
<FormField>
|
||||
<Localized id="configure-auth-clientID">
|
||||
<Label>Client ID</Label>
|
||||
</Localized>
|
||||
<Field name={name} parse={parseEmptyAsNull} validate={validate}>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
meta={meta}
|
||||
{...input}
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
/>
|
||||
<>
|
||||
<Localized id="configure-auth-clientID">
|
||||
<Label htmlFor={input.name}>Client ID</Label>
|
||||
</Localized>
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={input.name}
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
meta={meta}
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</FormField>
|
||||
|
||||
@@ -18,9 +18,6 @@ const ClientSecretField: FunctionComponent<Props> = ({
|
||||
validate,
|
||||
}) => (
|
||||
<FormField>
|
||||
<Localized id="configure-auth-clientSecret">
|
||||
<Label>Client secret</Label>
|
||||
</Localized>
|
||||
<Field
|
||||
name={name}
|
||||
key={(disabled && "on") || "off"}
|
||||
@@ -29,13 +26,17 @@ const ClientSecretField: FunctionComponent<Props> = ({
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<Localized id="configure-auth-clientSecret">
|
||||
<Label htmlFor={input.name}>Client secret</Label>
|
||||
</Localized>
|
||||
<PasswordField
|
||||
{...input}
|
||||
id={input.name}
|
||||
disabled={disabled || meta.submitting}
|
||||
// TODO: (wyattjoh) figure out how to add translations to these props
|
||||
hidePasswordTitle="Show Client Secret"
|
||||
showPasswordTitle="Hide Client Secret"
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
<ValidationMessage meta={meta} fullWidth />
|
||||
</>
|
||||
|
||||
@@ -32,7 +32,7 @@ const ConfigBoxWithToggleField: FunctionComponent<Props> = ({
|
||||
topRight={
|
||||
<FormField>
|
||||
<Localized id="configure-auth-configBoxEnabled">
|
||||
<CheckBox id={input.name} disabled={disabled} light {...input}>
|
||||
<CheckBox {...input} id={input.name} disabled={disabled} light>
|
||||
Enabled
|
||||
</CheckBox>
|
||||
</Localized>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
Condition,
|
||||
@@ -17,6 +18,24 @@ import RedirectField from "./RedirectField";
|
||||
import RegistrationField from "./RegistrationField";
|
||||
import TargetFilterField from "./TargetFilterField";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment FacebookConfig_formValues on Auth {
|
||||
integrations {
|
||||
facebook {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
clientID
|
||||
clientSecret
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
callbackURL: string;
|
||||
@@ -29,7 +48,12 @@ const FacebookLink = () => (
|
||||
);
|
||||
|
||||
const isEnabled: Condition = (value, values) =>
|
||||
Boolean(values.auth.integrations.facebook.enabled);
|
||||
Boolean(
|
||||
values.auth &&
|
||||
values.auth.integrations &&
|
||||
values.auth.integrations.facebook &&
|
||||
values.auth.integrations.facebook.enabled
|
||||
);
|
||||
|
||||
const FacebookConfig: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
|
||||
@@ -4,54 +4,29 @@ import { graphql } from "react-relay";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { FacebookConfigContainer_auth as AuthData } from "coral-admin/__generated__/FacebookConfigContainer_auth.graphql";
|
||||
import { FacebookConfigContainer_authReadOnly as AuthReadOnlyData } from "coral-admin/__generated__/FacebookConfigContainer_authReadOnly.graphql";
|
||||
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import FacebookConfig from "./FacebookConfig";
|
||||
|
||||
interface Props {
|
||||
auth: AuthData;
|
||||
authReadOnly: AuthReadOnlyData;
|
||||
onInitValues: OnInitValuesFct;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
class FacebookConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues({ auth: props.auth });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled, authReadOnly } = this.props;
|
||||
return (
|
||||
<FacebookConfig
|
||||
disabled={disabled}
|
||||
callbackURL={authReadOnly.integrations.facebook.callbackURL}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
const FacebookConfigContainer: React.FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
auth,
|
||||
}) => {
|
||||
return (
|
||||
<FacebookConfig
|
||||
disabled={disabled}
|
||||
callbackURL={auth.integrations.facebook.callbackURL}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
auth: graphql`
|
||||
fragment FacebookConfigContainer_auth on Auth {
|
||||
integrations {
|
||||
facebook {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
clientID
|
||||
clientSecret
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
authReadOnly: graphql`
|
||||
fragment FacebookConfigContainer_authReadOnly on Auth {
|
||||
integrations {
|
||||
facebook {
|
||||
callbackURL
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
Condition,
|
||||
@@ -17,6 +18,23 @@ import RedirectField from "./RedirectField";
|
||||
import RegistrationField from "./RegistrationField";
|
||||
import TargetFilterField from "./TargetFilterField";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment GoogleConfig_formValues on Auth {
|
||||
integrations {
|
||||
google {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
clientID
|
||||
clientSecret
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
callbackURL: string;
|
||||
@@ -42,6 +60,7 @@ const GoogleConfig: FunctionComponent<Props> = ({ disabled, callbackURL }) => (
|
||||
}
|
||||
name="auth.integrations.google.enabled"
|
||||
disabled={disabled}
|
||||
data-testid="configure-auth-google"
|
||||
>
|
||||
{disabledInside => (
|
||||
<>
|
||||
|
||||
@@ -4,54 +4,29 @@ import { graphql } from "react-relay";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { GoogleConfigContainer_auth as AuthData } from "coral-admin/__generated__/GoogleConfigContainer_auth.graphql";
|
||||
import { GoogleConfigContainer_authReadOnly as AuthReadOnlyData } from "coral-admin/__generated__/GoogleConfigContainer_authReadOnly.graphql";
|
||||
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import GoogleConfig from "./GoogleConfig";
|
||||
|
||||
interface Props {
|
||||
auth: AuthData;
|
||||
authReadOnly: AuthReadOnlyData;
|
||||
onInitValues: OnInitValuesFct;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
class GoogleConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues({ auth: props.auth });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled, authReadOnly } = this.props;
|
||||
return (
|
||||
<GoogleConfig
|
||||
disabled={disabled}
|
||||
callbackURL={authReadOnly.integrations.google.callbackURL}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
const GoogleConfigContainer: React.FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
auth,
|
||||
}) => {
|
||||
return (
|
||||
<GoogleConfig
|
||||
disabled={disabled}
|
||||
callbackURL={auth.integrations.google.callbackURL}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
auth: graphql`
|
||||
fragment GoogleConfigContainer_auth on Auth {
|
||||
integrations {
|
||||
google {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
clientID
|
||||
clientSecret
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
authReadOnly: graphql`
|
||||
fragment GoogleConfigContainer_authReadOnly on Auth {
|
||||
integrations {
|
||||
google {
|
||||
callbackURL
|
||||
|
||||
@@ -1,11 +1,28 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import Header from "../../Header";
|
||||
import ConfigBoxWithToggleField from "./ConfigBoxWithToggleField";
|
||||
import RegistrationField from "./RegistrationField";
|
||||
import TargetFilterField from "./TargetFilterField";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment LocalAuthConfig_formValues on Auth {
|
||||
integrations {
|
||||
local {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
}
|
||||
@@ -19,6 +36,7 @@ const LocalAuthConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
}
|
||||
name="auth.integrations.local.enabled"
|
||||
disabled={disabled}
|
||||
data-testid="configure-auth-local"
|
||||
>
|
||||
{disabledInside => (
|
||||
<>
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { LocalAuthConfigContainer_auth as AuthData } from "coral-admin/__generated__/LocalAuthConfigContainer_auth.graphql";
|
||||
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import LocalAuthConfig from "./LocalAuthConfig";
|
||||
|
||||
interface Props {
|
||||
auth: AuthData;
|
||||
onInitValues: OnInitValuesFct;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
class LocalAuthConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues({ auth: props.auth });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <LocalAuthConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
auth: graphql`
|
||||
fragment LocalAuthConfigContainer_auth on Auth {
|
||||
integrations {
|
||||
local {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(LocalAuthConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { colorFromMeta, parseEmptyAsNull } from "coral-framework/lib/form";
|
||||
import {
|
||||
@@ -33,6 +34,29 @@ import RedirectField from "./RedirectField";
|
||||
import RegistrationField from "./RegistrationField";
|
||||
import TargetFilterField from "./TargetFilterField";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment OIDCConfig_formValues on Auth {
|
||||
integrations {
|
||||
oidc {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
name
|
||||
clientID
|
||||
clientSecret
|
||||
authorizationURL
|
||||
tokenURL
|
||||
jwksURI
|
||||
issuer
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
callbackURL: string;
|
||||
@@ -76,7 +100,9 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
<FormField>
|
||||
<FormFieldHeader>
|
||||
<Localized id="configure-auth-oidc-providerName">
|
||||
<Label>Provider name</Label>
|
||||
<Label htmlFor="auth.integrations.oidc.name">
|
||||
Provider name
|
||||
</Label>
|
||||
</Localized>
|
||||
<Localized id="configure-auth-oidc-providerNameDescription">
|
||||
<HelperText>
|
||||
@@ -94,6 +120,8 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={input.name}
|
||||
disabled={disabledInside}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
@@ -102,7 +130,6 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
color={colorFromMeta(meta)}
|
||||
fullWidth
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
@@ -120,7 +147,7 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
<FormField>
|
||||
<FormFieldHeader>
|
||||
<Localized id="configure-auth-oidc-issuer">
|
||||
<Label>Issuer</Label>
|
||||
<Label htmlFor="auth.integrations.oidc.issuer">Issuer</Label>
|
||||
</Localized>
|
||||
<Localized id="configure-auth-oidc-issuerDescription">
|
||||
<HelperText>
|
||||
@@ -139,6 +166,8 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
<>
|
||||
<Flex direction="row" itemGutter="half" alignItems="center">
|
||||
<TextField
|
||||
{...input}
|
||||
id={input.name}
|
||||
disabled={disabledInside || disableForDiscover}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
@@ -146,7 +175,6 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
spellCheck={false}
|
||||
color={colorFromMeta(meta)}
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
<Button
|
||||
id="configure-auth-oidc-discover"
|
||||
@@ -174,6 +202,8 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={input.name}
|
||||
disabled={disabledInside || disableForDiscover}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
@@ -181,7 +211,6 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
@@ -197,6 +226,8 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={input.name}
|
||||
disabled={disabledInside || disableForDiscover}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
@@ -204,7 +235,6 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
spellCheck={false}
|
||||
meta={meta}
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
@@ -220,6 +250,8 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={input.name}
|
||||
disabled={disabledInside || disableForDiscover}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
@@ -227,7 +259,6 @@ const OIDCConfig: FunctionComponent<Props> = ({
|
||||
spellCheck={false}
|
||||
meta={meta}
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
import { FormApi } from "final-form";
|
||||
import React from "react";
|
||||
import { ReactContext, withReactFinalForm } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
import { InferableComponentEnhancer } from "recompose";
|
||||
|
||||
import { withForm } from "coral-framework/lib/form";
|
||||
import {
|
||||
FetchProp,
|
||||
withFetch,
|
||||
withFragmentContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
|
||||
import { OIDCConfig_formValues } from "coral-admin/__generated__/OIDCConfig_formValues.graphql";
|
||||
import { OIDCConfigContainer_auth as AuthData } from "coral-admin/__generated__/OIDCConfigContainer_auth.graphql";
|
||||
import { OIDCConfigContainer_authReadOnly as AuthReadOnlyData } from "coral-admin/__generated__/OIDCConfigContainer_authReadOnly.graphql";
|
||||
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import DiscoverOIDCConfigurationFetch from "./DiscoverOIDCConfigurationFetch";
|
||||
import OIDCConfig from "./OIDCConfig";
|
||||
|
||||
interface Props {
|
||||
auth: AuthData;
|
||||
authReadOnly: AuthReadOnlyData;
|
||||
onInitValues: OnInitValuesFct;
|
||||
disabled?: boolean;
|
||||
discoverOIDCConfiguration: FetchProp<typeof DiscoverOIDCConfigurationFetch>;
|
||||
reactFinalForm: FormApi;
|
||||
form: FormApi<{ auth: AuthData & OIDCConfig_formValues }>;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@@ -36,22 +32,35 @@ class OIDCConfigContainer extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
private handleDiscover = async () => {
|
||||
const form = this.props.reactFinalForm;
|
||||
const issuer = this.props.form.getState().values.auth.integrations.oidc
|
||||
.issuer;
|
||||
if (!issuer) {
|
||||
return;
|
||||
}
|
||||
this.setState({ awaitingResponse: true });
|
||||
try {
|
||||
const config = await this.props.discoverOIDCConfiguration({
|
||||
issuer: form.getState().values.auth.integrations.oidc.issuer,
|
||||
issuer,
|
||||
});
|
||||
if (config) {
|
||||
if (config.issuer) {
|
||||
form.change("auth.integrations.oidc.issuer", config.issuer);
|
||||
this.props.form.change(
|
||||
"auth.integrations.oidc.issuer",
|
||||
config.issuer
|
||||
);
|
||||
}
|
||||
form.change(
|
||||
this.props.form.change(
|
||||
"auth.integrations.oidc.authorizationURL",
|
||||
config.authorizationURL
|
||||
);
|
||||
form.change("auth.integrations.oidc.jwksURI", config.jwksURI);
|
||||
form.change("auth.integrations.oidc.tokenURL", config.tokenURL);
|
||||
this.props.form.change(
|
||||
"auth.integrations.oidc.jwksURI",
|
||||
config.jwksURI
|
||||
);
|
||||
this.props.form.change(
|
||||
"auth.integrations.oidc.tokenURL",
|
||||
config.tokenURL
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// FIXME: (wyattjoh) handle error
|
||||
@@ -61,17 +70,12 @@ class OIDCConfigContainer extends React.Component<Props, State> {
|
||||
this.setState({ awaitingResponse: false });
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues({ auth: props.auth });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled, authReadOnly } = this.props;
|
||||
const { disabled, auth } = this.props;
|
||||
return (
|
||||
<OIDCConfig
|
||||
disabled={disabled}
|
||||
callbackURL={authReadOnly.integrations.oidc.callbackURL}
|
||||
callbackURL={auth.integrations.oidc.callbackURL}
|
||||
onDiscover={this.handleDiscover}
|
||||
disableForDiscover={this.state.awaitingResponse}
|
||||
/>
|
||||
@@ -79,36 +83,11 @@ class OIDCConfigContainer extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
// (cvle) Fix `withReactFinalForm` typings (v4.1.0), forgive this, we'll
|
||||
// probably only use hooks in the future instead anyway ;-)
|
||||
const withForm = withReactFinalForm as InferableComponentEnhancer<ReactContext>;
|
||||
|
||||
const enhanced = withForm(
|
||||
withFetch(DiscoverOIDCConfigurationFetch)(
|
||||
withFragmentContainer<Props>({
|
||||
auth: graphql`
|
||||
fragment OIDCConfigContainer_auth on Auth {
|
||||
integrations {
|
||||
oidc {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
name
|
||||
clientID
|
||||
clientSecret
|
||||
authorizationURL
|
||||
tokenURL
|
||||
jwksURI
|
||||
issuer
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
authReadOnly: graphql`
|
||||
fragment OIDCConfigContainer_authReadOnly on Auth {
|
||||
integrations {
|
||||
oidc {
|
||||
callbackURL
|
||||
|
||||
@@ -32,7 +32,7 @@ const RegistrationField: FunctionComponent<Props> = ({ name, disabled }) => (
|
||||
<Field name={name} type="checkbox">
|
||||
{({ input }) => (
|
||||
<Localized id="configure-auth-registrationCheckBox">
|
||||
<CheckBox id={input.name} disabled={disabled} {...input}>
|
||||
<CheckBox {...input} id={input.name} disabled={disabled}>
|
||||
Allow Registration
|
||||
</CheckBox>
|
||||
</Localized>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
|
||||
@@ -9,6 +10,22 @@ import RegistrationField from "./RegistrationField";
|
||||
import SSOKeyFieldContainer from "./SSOKeyFieldContainer";
|
||||
import TargetFilterField from "./TargetFilterField";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment SSOConfig_formValues on Auth {
|
||||
integrations {
|
||||
sso {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
sso: PropTypesOf<typeof SSOKeyFieldContainer>["sso"];
|
||||
@@ -23,6 +40,7 @@ const SSOConfig: FunctionComponent<Props> = ({ disabled, sso }) => (
|
||||
}
|
||||
name="auth.integrations.sso.enabled"
|
||||
disabled={disabled}
|
||||
data-testid="configure-auth-sso"
|
||||
>
|
||||
{disabledInside => (
|
||||
<>
|
||||
|
||||
@@ -4,52 +4,24 @@ import { graphql } from "react-relay";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { SSOConfigContainer_auth as AuthData } from "coral-admin/__generated__/SSOConfigContainer_auth.graphql";
|
||||
import { SSOConfigContainer_authReadOnly as AuthReadOnlyData } from "coral-admin/__generated__/SSOConfigContainer_authReadOnly.graphql";
|
||||
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import SSOConfig from "./SSOConfig";
|
||||
|
||||
interface Props {
|
||||
auth: AuthData;
|
||||
authReadOnly: AuthReadOnlyData;
|
||||
onInitValues: OnInitValuesFct;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
class SSOConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues({ auth: props.auth });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return (
|
||||
<SSOConfig
|
||||
disabled={disabled}
|
||||
sso={this.props.authReadOnly.integrations.sso}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
const SSOConfigContainer: React.FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
auth,
|
||||
}) => {
|
||||
return <SSOConfig disabled={disabled} sso={auth.integrations.sso} />;
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
auth: graphql`
|
||||
fragment SSOConfigContainer_auth on Auth {
|
||||
integrations {
|
||||
sso {
|
||||
enabled
|
||||
allowRegistration
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
authReadOnly: graphql`
|
||||
fragment SSOConfigContainer_authReadOnly on Auth {
|
||||
integrations {
|
||||
sso {
|
||||
...SSOKeyFieldContainer_sso
|
||||
|
||||
@@ -27,11 +27,12 @@ const SSOKeyField: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
onRegenerate,
|
||||
}) => (
|
||||
<FormField className={styles.root} data-testid="configure-auth-sso-key">
|
||||
<FormField className={styles.root}>
|
||||
<Localized id="configure-auth-sso-key">
|
||||
<Label>Key</Label>
|
||||
<Label htmlFor="configure-auth-sso-key">Key</Label>
|
||||
</Localized>
|
||||
<PasswordField
|
||||
id="configure-auth-sso-key"
|
||||
name="key"
|
||||
value={generatedKey}
|
||||
readOnly
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { colorFromMeta } from "coral-framework/lib/form";
|
||||
import {
|
||||
@@ -19,6 +20,13 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import ValidationMessage from "../../ValidationMessage";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment SessionConfig_formValues on Auth {
|
||||
sessionDuration
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
}
|
||||
@@ -45,9 +53,9 @@ const SessionConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<DurationField
|
||||
{...input}
|
||||
color={colorFromMeta(meta)}
|
||||
disabled={!!disabled}
|
||||
{...input}
|
||||
/>
|
||||
<ValidationMessage meta={meta} fullWidth />
|
||||
</>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { SessionConfigContainer_auth as AuthData } from "coral-admin/__generated__/SessionConfigContainer_auth.graphql";
|
||||
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import SessionConfig from "./SessionConfig";
|
||||
|
||||
interface Props {
|
||||
auth: AuthData;
|
||||
onInitValues: OnInitValuesFct;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
class SessionConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues({ auth: props.auth });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <SessionConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
auth: graphql`
|
||||
fragment SessionConfigContainer_auth on Auth {
|
||||
sessionDuration
|
||||
}
|
||||
`,
|
||||
})(SessionConfigContainer);
|
||||
export default enhanced;
|
||||
@@ -22,7 +22,7 @@ const TargetFilterField: FunctionComponent<Props> = ({
|
||||
<Field name={`${name}.admin`} type="checkbox" parse={parseBool}>
|
||||
{({ input }) => (
|
||||
<Localized id="configure-auth-targetFilterCoralAdmin">
|
||||
<CheckBox id={input.name} disabled={disabled} {...input}>
|
||||
<CheckBox {...input} id={input.name} disabled={disabled}>
|
||||
Coral Admin
|
||||
</CheckBox>
|
||||
</Localized>
|
||||
@@ -31,7 +31,7 @@ const TargetFilterField: FunctionComponent<Props> = ({
|
||||
<Field name={`${name}.stream`} type="checkbox" parse={parseBool}>
|
||||
{({ input }) => (
|
||||
<Localized id="configure-auth-targetFilterCommentStream">
|
||||
<CheckBox id={input.name} disabled={disabled} {...input}>
|
||||
<CheckBox {...input} id={input.name} disabled={disabled}>
|
||||
Comment Stream
|
||||
</CheckBox>
|
||||
</Localized>
|
||||
|
||||
@@ -1,108 +1,80 @@
|
||||
import { FormApi } from "final-form";
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import { RouteProps } from "found";
|
||||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { useForm } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { DeepNullable, DeepPartial } from "coral-common/types";
|
||||
import { pureMerge } from "coral-common/utils";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import { DeepNullable } from "coral-common/types";
|
||||
import {
|
||||
purgeMetadata,
|
||||
withFragmentContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { GQLSettings } from "coral-framework/schema";
|
||||
|
||||
import { EmailConfigContainer_email } from "coral-admin/__generated__/EmailConfigContainer_email.graphql";
|
||||
|
||||
import Header from "../../Header";
|
||||
import ConfigBoxWithToggleField from "../Auth/ConfigBoxWithToggleField";
|
||||
import FromContainer from "./FromContainer";
|
||||
import SMTPContainer from "./SMTPContainer";
|
||||
import From from "./From";
|
||||
import SMTP from "./SMTP";
|
||||
|
||||
interface Props {
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
email: EmailConfigContainer_email;
|
||||
}
|
||||
|
||||
export type FormProps = DeepNullable<Pick<GQLSettings, "email">>;
|
||||
export type OnInitValuesFct = (values: DeepPartial<FormProps>) => void;
|
||||
|
||||
class EmailConfigContainer extends React.Component<Props> {
|
||||
public static routeConfig: RouteProps;
|
||||
private initialValues: DeepPartial<FormProps> = {};
|
||||
|
||||
public componentDidMount() {
|
||||
this.props.form.initialize(this.initialValues);
|
||||
}
|
||||
|
||||
private handleOnInitValues: OnInitValuesFct = values => {
|
||||
if (
|
||||
values.email &&
|
||||
values.email.smtp &&
|
||||
values.email.smtp.authentication === null
|
||||
) {
|
||||
const EmailConfigContainer: React.FunctionComponent<Props> = ({
|
||||
email,
|
||||
submitting,
|
||||
}) => {
|
||||
const form = useForm();
|
||||
useMemo(() => {
|
||||
let values: any = { email };
|
||||
if (email && email.smtp && email.smtp.authentication === null) {
|
||||
values = {
|
||||
email: {
|
||||
...values.email,
|
||||
smtp: { ...values.email.smtp, authentication: true },
|
||||
...email,
|
||||
smtp: { ...email.smtp, authentication: true },
|
||||
},
|
||||
};
|
||||
}
|
||||
if (
|
||||
values.email &&
|
||||
values.email.smtp &&
|
||||
values.email.smtp.secure === null
|
||||
) {
|
||||
if (email && email.smtp && email.smtp.secure === null) {
|
||||
values = {
|
||||
email: {
|
||||
...values.email,
|
||||
smtp: { ...values.email.smtp, secure: true },
|
||||
...email,
|
||||
smtp: { ...email.smtp, secure: true },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
this.initialValues = pureMerge<DeepPartial<FormProps>>(
|
||||
this.initialValues,
|
||||
values
|
||||
);
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { email, submitting } = this.props;
|
||||
|
||||
return (
|
||||
<ConfigBoxWithToggleField
|
||||
disabled={submitting}
|
||||
title={
|
||||
<Localized id="configure-email">
|
||||
<Header container="h2">Email settings</Header>
|
||||
</Localized>
|
||||
}
|
||||
name="email.enabled"
|
||||
>
|
||||
{disabledInside => (
|
||||
<>
|
||||
<FromContainer
|
||||
email={email}
|
||||
disabled={disabledInside}
|
||||
onInitValues={this.handleOnInitValues}
|
||||
/>
|
||||
<SMTPContainer
|
||||
email={email}
|
||||
disabled={disabledInside}
|
||||
onInitValues={this.handleOnInitValues}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</ConfigBoxWithToggleField>
|
||||
);
|
||||
}
|
||||
}
|
||||
form.initialize(purgeMetadata(values));
|
||||
}, []);
|
||||
return (
|
||||
<ConfigBoxWithToggleField
|
||||
disabled={submitting}
|
||||
title={
|
||||
<Localized id="configure-email">
|
||||
<Header container="h2">Email settings</Header>
|
||||
</Localized>
|
||||
}
|
||||
name="email.enabled"
|
||||
>
|
||||
{disabledInside => (
|
||||
<>
|
||||
<From disabled={disabledInside} />
|
||||
<SMTP disabled={disabledInside} />
|
||||
</>
|
||||
)}
|
||||
</ConfigBoxWithToggleField>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
email: graphql`
|
||||
fragment EmailConfigContainer_email on EmailConfiguration {
|
||||
enabled
|
||||
...FromContainer_email
|
||||
...SMTPContainer_email
|
||||
...From_formValues @relay(mask: false)
|
||||
...SMTP_formValues @relay(mask: false)
|
||||
}
|
||||
`,
|
||||
})(EmailConfigContainer);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { FormApi } from "final-form";
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
@@ -11,7 +10,6 @@ import EmailConfigContainer from "./EmailConfigContainer";
|
||||
|
||||
interface Props {
|
||||
data: EmailConfigRouteQueryResponse | null;
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
}
|
||||
|
||||
@@ -27,7 +25,6 @@ class EmailConfigRoute extends React.Component<Props> {
|
||||
return (
|
||||
<EmailConfigContainer
|
||||
email={this.props.data.settings.email}
|
||||
form={this.props.form}
|
||||
submitting={this.props.submitting}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { parseEmptyAsNull } from "coral-framework/lib/form";
|
||||
import { validateEmail } from "coral-framework/lib/validation";
|
||||
@@ -13,6 +14,15 @@ import {
|
||||
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment From_formValues on EmailConfiguration {
|
||||
enabled
|
||||
fromName
|
||||
fromEmail
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -31,10 +41,10 @@ const From: FunctionComponent<Props> = ({ disabled }) => (
|
||||
<Field name="email.fromName" parse={parseEmptyAsNull}>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
fullWidth
|
||||
disabled={disabled}
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
@@ -57,11 +67,11 @@ const From: FunctionComponent<Props> = ({ disabled }) => (
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
type="email"
|
||||
fullWidth
|
||||
meta={meta}
|
||||
disabled={disabled}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { FromContainer_email } from "coral-admin/__generated__/FromContainer_email.graphql";
|
||||
|
||||
import { OnInitValuesFct } from "./EmailConfigContainer";
|
||||
import From from "./From";
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
onInitValues: OnInitValuesFct;
|
||||
email: FromContainer_email;
|
||||
}
|
||||
|
||||
class FromContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues({ email: props.email });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <From disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
email: graphql`
|
||||
fragment FromContainer_email on EmailConfiguration {
|
||||
enabled
|
||||
fromName
|
||||
fromEmail
|
||||
}
|
||||
`,
|
||||
})(FromContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
colorFromMeta,
|
||||
@@ -25,12 +26,26 @@ import Subheader from "../../Subheader";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
import { FormProps } from "./EmailConfigContainer";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment SMTP_formValues on EmailConfiguration {
|
||||
enabled
|
||||
smtp {
|
||||
host
|
||||
port
|
||||
secure
|
||||
authentication
|
||||
username
|
||||
password
|
||||
}
|
||||
}
|
||||
`;
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const isEnabled: Condition<any, FormProps> = (value, values) =>
|
||||
Boolean(values.email.enabled);
|
||||
Boolean(values.email && values.email.enabled);
|
||||
|
||||
const isAuthenticating: Condition<any, FormProps> = (value, values) =>
|
||||
Boolean(values.email.enabled && values.email.smtp.authentication);
|
||||
@@ -52,11 +67,11 @@ const SMTP: FunctionComponent<Props> = ({ disabled }) => (
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={input.name}
|
||||
fullWidth
|
||||
disabled={disabled}
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
@@ -76,12 +91,12 @@ const SMTP: FunctionComponent<Props> = ({ disabled }) => (
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={input.name}
|
||||
type="number"
|
||||
fullWidth
|
||||
disabled={disabled}
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
@@ -123,11 +138,11 @@ const SMTP: FunctionComponent<Props> = ({ disabled }) => (
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={input.name}
|
||||
disabled={disabled || !enabled}
|
||||
fullWidth
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
@@ -144,11 +159,11 @@ const SMTP: FunctionComponent<Props> = ({ disabled }) => (
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<PasswordField
|
||||
{...input}
|
||||
id={input.name}
|
||||
disabled={disabled || !enabled}
|
||||
fullWidth
|
||||
color={colorFromMeta(meta)}
|
||||
{...input}
|
||||
/>
|
||||
<ValidationMessage fullWidth meta={meta} />
|
||||
</>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { SMTPContainer_email } from "coral-admin/__generated__/SMTPContainer_email.graphql";
|
||||
|
||||
import { OnInitValuesFct } from "./EmailConfigContainer";
|
||||
import SMTP from "./SMTP";
|
||||
|
||||
interface Props {
|
||||
email: SMTPContainer_email;
|
||||
disabled: boolean;
|
||||
onInitValues: OnInitValuesFct;
|
||||
}
|
||||
|
||||
class SMTPContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues({ email: props.email });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <SMTP disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
email: graphql`
|
||||
fragment SMTPContainer_email on EmailConfiguration {
|
||||
enabled
|
||||
smtp {
|
||||
host
|
||||
port
|
||||
secure
|
||||
authentication
|
||||
username
|
||||
password
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(SMTPContainer);
|
||||
|
||||
export default enhanced;
|
||||
+11
-1
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent, Suspense } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { MarkdownEditor } from "coral-framework/components/loadables";
|
||||
import { parseEmptyAsNull } from "coral-framework/lib/form";
|
||||
@@ -10,6 +11,15 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import ValidationMessage from "../../ValidationMessage";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment ClosedStreamMessageConfig_formValues on Settings {
|
||||
closeCommenting {
|
||||
message
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -37,8 +47,8 @@ const ClosedStreamMessageConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
<>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<MarkdownEditor
|
||||
id="configure-general-closedStreamMessage-content"
|
||||
{...input}
|
||||
id="configure-general-closedStreamMessage-content"
|
||||
/>
|
||||
</Suspense>
|
||||
<ValidationMessage meta={meta} fullWidth />
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { ClosedStreamMessageConfigContainer_settings as SettingsData } from "coral-admin/__generated__/ClosedStreamMessageConfigContainer_settings.graphql";
|
||||
|
||||
import ClosedStreamMessageConfig from "./ClosedStreamMessageConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class ClosedStreamMessageConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <ClosedStreamMessageConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment ClosedStreamMessageConfigContainer_settings on Settings {
|
||||
closeCommenting {
|
||||
message
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(ClosedStreamMessageConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
+12
-1
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { colorFromMeta } from "coral-framework/lib/form";
|
||||
import {
|
||||
@@ -22,6 +23,16 @@ import Header from "../../Header";
|
||||
import OnOffField from "../../OnOffField";
|
||||
import ValidationMessage from "../../ValidationMessage";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment ClosingCommentStreamsConfig_formValues on Settings {
|
||||
closeCommenting {
|
||||
auto
|
||||
timeout
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -67,6 +78,7 @@ const ClosingCommentStreamsConfig: FunctionComponent<Props> = ({
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<DurationField
|
||||
{...input}
|
||||
units={[
|
||||
DURATION_UNIT.HOURS,
|
||||
DURATION_UNIT.DAYS,
|
||||
@@ -74,7 +86,6 @@ const ClosingCommentStreamsConfig: FunctionComponent<Props> = ({
|
||||
]}
|
||||
disabled={disabled}
|
||||
color={colorFromMeta(meta)}
|
||||
{...input}
|
||||
/>
|
||||
<ValidationMessage meta={meta} fullWidth />
|
||||
</>
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { ClosingCommentStreamsConfigContainer_settings as SettingsData } from "coral-admin/__generated__/ClosingCommentStreamsConfigContainer_settings.graphql";
|
||||
|
||||
import ClosingCommentStreamsConfig from "./ClosingCommentStreamsConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class ClosingCommentStreamsConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <ClosingCommentStreamsConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment ClosingCommentStreamsConfigContainer_settings on Settings {
|
||||
closeCommenting {
|
||||
auto
|
||||
timeout
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(ClosingCommentStreamsConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { colorFromMeta } from "coral-framework/lib/form";
|
||||
import {
|
||||
@@ -21,6 +22,12 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import ValidationMessage from "../../ValidationMessage";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment CommentEditingConfig_formValues on Settings {
|
||||
editCommentWindowLength
|
||||
}
|
||||
`;
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -58,6 +65,7 @@ const CommentEditingConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<DurationField
|
||||
{...input}
|
||||
units={[
|
||||
DURATION_UNIT.SECONDS,
|
||||
DURATION_UNIT.MINUTES,
|
||||
@@ -65,7 +73,6 @@ const CommentEditingConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
]}
|
||||
color={colorFromMeta(meta)}
|
||||
disabled={disabled}
|
||||
{...input}
|
||||
/>
|
||||
<ValidationMessage meta={meta} fullWidth />
|
||||
</>
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { CommentEditingConfigContainer_settings as SettingsData } from "coral-admin/__generated__/CommentEditingConfigContainer_settings.graphql";
|
||||
|
||||
import CommentEditingConfig from "./CommentEditingConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class CommentEditingConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <CommentEditingConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment CommentEditingConfigContainer_settings on Settings {
|
||||
editCommentWindowLength
|
||||
}
|
||||
`,
|
||||
})(CommentEditingConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { formatEmpty, parseEmptyAsNull } from "coral-framework/lib/form";
|
||||
import {
|
||||
@@ -23,8 +24,19 @@ import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
import styles from "./CommentLengthConfig.css";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment CommentLengthConfig_formValues on Settings {
|
||||
charCount {
|
||||
enabled
|
||||
min
|
||||
max
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const validateMaxLongerThanMin = createValidator(
|
||||
(v, values) =>
|
||||
(v: any, values: any) =>
|
||||
v === null ||
|
||||
values.charCount.min === null ||
|
||||
parseInt(v, 10) > parseInt(values.charCount.min, 10),
|
||||
@@ -82,11 +94,11 @@ const CommentLengthConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
attrs={{ placeholder: true }}
|
||||
>
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id="configure-general-commentLength-min"
|
||||
classes={{
|
||||
input: styles.commentLengthTextInput,
|
||||
}}
|
||||
{...input}
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
meta={meta}
|
||||
@@ -126,6 +138,7 @@ const CommentLengthConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
attrs={{ placeholder: true }}
|
||||
>
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id="configure-general-commentLength-max"
|
||||
classes={{
|
||||
input: styles.commentLengthTextInput,
|
||||
@@ -143,7 +156,6 @@ const CommentLengthConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
placeholder={"No limit"}
|
||||
textAlignCenter
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
</Localized>
|
||||
)}
|
||||
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { CommentLengthConfigContainer_settings as SettingsData } from "coral-admin/__generated__/CommentLengthConfigContainer_settings.graphql";
|
||||
|
||||
import CommentLengthConfig from "./CommentLengthConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class CommentLengthConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <CommentLengthConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment CommentLengthConfigContainer_settings on Settings {
|
||||
charCount {
|
||||
enabled
|
||||
min
|
||||
max
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(CommentLengthConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,84 +0,0 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import ClosedStreamMessageConfigContainer from "./ClosedStreamMessageConfigContainer";
|
||||
import ClosingCommentStreamsConfigContainer from "./ClosingCommentStreamsConfigContainer";
|
||||
import CommentEditingConfigContainer from "./CommentEditingConfigContainer";
|
||||
import CommentLengthConfigContainer from "./CommentLengthConfigContainer";
|
||||
import GuidelinesConfigContainer from "./GuidelinesConfigContainer";
|
||||
import LocaleConfigContainer from "./LocaleConfigContainer";
|
||||
import ReactionConfigContainer from "./ReactionConfigContainer";
|
||||
import SitewideCommentingConfigContainer from "./SitewideCommentingConfigContainer";
|
||||
import StaffConfigContainer from "./StaffConfigContainer";
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
settings: PropTypesOf<typeof GuidelinesConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof CommentLengthConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof CommentEditingConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof ClosedStreamMessageConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof ReactionConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof StaffConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof ClosingCommentStreamsConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof SitewideCommentingConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof LocaleConfigContainer>["settings"];
|
||||
onInitValues: (values: any) => void;
|
||||
}
|
||||
|
||||
const General: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
settings,
|
||||
onInitValues,
|
||||
}) => (
|
||||
<HorizontalGutter size="double" data-testid="configure-generalContainer">
|
||||
<LocaleConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<SitewideCommentingConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<GuidelinesConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<CommentLengthConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<CommentEditingConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<ClosingCommentStreamsConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<ClosedStreamMessageConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<ReactionConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<StaffConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
export default General;
|
||||
+46
-41
@@ -1,60 +1,65 @@
|
||||
import { FormApi } from "final-form";
|
||||
import { RouteProps } from "found";
|
||||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { useForm } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { pureMerge } from "coral-common/utils";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import {
|
||||
purgeMetadata,
|
||||
withFragmentContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { HorizontalGutter } from "coral-ui/components";
|
||||
|
||||
import { GeneralConfigContainer_settings as SettingsData } from "coral-admin/__generated__/GeneralConfigContainer_settings.graphql";
|
||||
|
||||
import GeneralConfig from "./GeneralConfig";
|
||||
import ClosedStreamMessageConfig from "./ClosedStreamMessageConfig";
|
||||
import ClosingCommentStreamsConfig from "./ClosingCommentStreamsConfig";
|
||||
import CommentEditingConfig from "./CommentEditingConfig";
|
||||
import CommentLengthConfig from "./CommentLengthConfig";
|
||||
import GuidelinesConfig from "./GuidelinesConfig";
|
||||
import LocaleConfig from "./LocaleConfig";
|
||||
import ReactionConfigContainer from "./ReactionConfigContainer";
|
||||
import SitewideCommentingConfig from "./SitewideCommentingConfig";
|
||||
import StaffConfig from "./StaffConfig";
|
||||
|
||||
interface Props {
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
settings: SettingsData;
|
||||
}
|
||||
|
||||
class GeneralConfigContainer extends React.Component<Props> {
|
||||
public static routeConfig: RouteProps;
|
||||
private initialValues = {};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.props.form.initialize(this.initialValues);
|
||||
}
|
||||
|
||||
private handleOnInitValues = (values: any) => {
|
||||
this.initialValues = pureMerge(this.initialValues, values);
|
||||
};
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<GeneralConfig
|
||||
disabled={this.props.submitting}
|
||||
settings={this.props.settings}
|
||||
onInitValues={this.handleOnInitValues}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
const GeneralConfigContainer: React.FunctionComponent<Props> = ({
|
||||
settings,
|
||||
submitting,
|
||||
}) => {
|
||||
const form = useForm();
|
||||
useMemo(() => form.initialize(purgeMetadata(settings)), []);
|
||||
return (
|
||||
<HorizontalGutter size="double" data-testid="configure-generalContainer">
|
||||
<LocaleConfig disabled={submitting} />
|
||||
<SitewideCommentingConfig disabled={submitting} />
|
||||
<GuidelinesConfig disabled={submitting} />
|
||||
<CommentLengthConfig disabled={submitting} />
|
||||
<CommentEditingConfig disabled={submitting} />
|
||||
<ClosingCommentStreamsConfig disabled={submitting} />
|
||||
<ClosedStreamMessageConfig disabled={submitting} />
|
||||
<ReactionConfigContainer disabled={submitting} settings={settings} />
|
||||
<StaffConfig disabled={submitting} />
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment GeneralConfigContainer_settings on Settings {
|
||||
...LocaleConfigContainer_settings
|
||||
...GuidelinesConfigContainer_settings
|
||||
...CommentLengthConfigContainer_settings
|
||||
...CommentEditingConfigContainer_settings
|
||||
...ClosedStreamMessageConfigContainer_settings
|
||||
...ClosingCommentStreamsConfigContainer_settings
|
||||
...SitewideCommentingConfigContainer_settings
|
||||
...LocaleConfig_formValues @relay(mask: false)
|
||||
...GuidelinesConfig_formValues @relay(mask: false)
|
||||
...CommentLengthConfig_formValues @relay(mask: false)
|
||||
...CommentEditingConfig_formValues @relay(mask: false)
|
||||
...ClosedStreamMessageConfig_formValues @relay(mask: false)
|
||||
...ClosingCommentStreamsConfig_formValues @relay(mask: false)
|
||||
...SitewideCommentingConfig_formValues @relay(mask: false)
|
||||
...ReactionConfig_formValues @relay(mask: false)
|
||||
...StaffConfig_formValues @relay(mask: false)
|
||||
|
||||
...ReactionConfigContainer_settings
|
||||
...StaffConfigContainer_settings
|
||||
}
|
||||
`,
|
||||
})(GeneralConfigContainer);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { FormApi } from "final-form";
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
@@ -12,7 +11,6 @@ import GeneralConfigContainer from "./GeneralConfigContainer";
|
||||
|
||||
interface Props {
|
||||
data: GeneralConfigRouteQueryResponse | null;
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
}
|
||||
|
||||
@@ -28,7 +26,6 @@ class GeneralConfigRoute extends React.Component<Props> {
|
||||
return (
|
||||
<GeneralConfigContainer
|
||||
settings={this.props.data.settings}
|
||||
form={this.props.form}
|
||||
submitting={this.props.submitting}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent, Suspense } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { MarkdownEditor } from "coral-framework/components/loadables";
|
||||
import { parseEmptyAsNull } from "coral-framework/lib/form";
|
||||
@@ -19,6 +20,16 @@ import Header from "../../Header";
|
||||
import OnOffField from "../../OnOffField";
|
||||
import ValidationMessage from "../../ValidationMessage";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment GuidelinesConfig_formValues on Settings {
|
||||
communityGuidelines {
|
||||
enabled
|
||||
content
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -65,8 +76,8 @@ const GuidelinesConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
<>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<MarkdownEditor
|
||||
id="configure-general-guidelines-content"
|
||||
{...input}
|
||||
id="configure-general-guidelines-content"
|
||||
/>
|
||||
</Suspense>
|
||||
<ValidationMessage meta={meta} />
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { GuidelinesConfigContainer_settings as SettingsData } from "coral-admin/__generated__/GuidelinesConfigContainer_settings.graphql";
|
||||
|
||||
import GuidelinesConfig from "./GuidelinesConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class GuidelinesConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <GuidelinesConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment GuidelinesConfigContainer_settings on Settings {
|
||||
communityGuidelines {
|
||||
enabled
|
||||
content
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(GuidelinesConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
+11
-18
@@ -1,27 +1,28 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { useMemo } from "react";
|
||||
import React from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import { required } from "coral-framework/lib/validation";
|
||||
import { FormFieldDescription, HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import { LocaleConfigContainer_settings } from "coral-admin/__generated__/LocaleConfigContainer_settings.graphql";
|
||||
|
||||
import ConfigBox from "../../ConfigBox";
|
||||
import LocaleField from "../../Fields/LocaleField";
|
||||
import Header from "../../Header";
|
||||
import ValidationMessage from "../../ValidationMessage";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment LocaleConfig_formValues on Settings {
|
||||
locale
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
settings: LocaleConfigContainer_settings;
|
||||
onInitValues: (values: LocaleConfigContainer_settings) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const LocaleConfigContainer: React.FunctionComponent<Props> = props => {
|
||||
useMemo(() => props.onInitValues(props.settings), [props.onInitValues]);
|
||||
const LocaleConfig: React.FunctionComponent<Props> = props => {
|
||||
return (
|
||||
<ConfigBox
|
||||
title={
|
||||
@@ -40,9 +41,9 @@ const LocaleConfigContainer: React.FunctionComponent<Props> = props => {
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<LocaleField
|
||||
{...input}
|
||||
id={`configure-locale-${input.name}`}
|
||||
disabled={props.disabled}
|
||||
{...input}
|
||||
/>
|
||||
<ValidationMessage meta={meta} fullWidth />
|
||||
</>
|
||||
@@ -53,12 +54,4 @@ const LocaleConfigContainer: React.FunctionComponent<Props> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment LocaleConfigContainer_settings on Settings {
|
||||
locale
|
||||
}
|
||||
`,
|
||||
})(LocaleConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
export default LocaleConfig;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { required } from "coral-framework/lib/validation";
|
||||
import ReactionButton from "coral-stream/tabs/Comments/Comment/ReactionButton/ReactionButton";
|
||||
@@ -15,20 +16,36 @@ import {
|
||||
SelectField,
|
||||
} from "coral-ui/components/v2";
|
||||
|
||||
import { ReactionConfigContainer_settings as SettingsData } from "coral-admin/__generated__/ReactionConfigContainer_settings.graphql";
|
||||
|
||||
import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
import styles from "./ReactionConfig.css";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment ReactionConfig_formValues on Settings {
|
||||
reaction {
|
||||
label
|
||||
labelActive
|
||||
sortLabel
|
||||
icon
|
||||
iconActive
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
icon: string;
|
||||
iconActive: string | null;
|
||||
disabled: boolean;
|
||||
settings: SettingsData;
|
||||
}
|
||||
|
||||
const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
const ReactionsConfig: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
icon,
|
||||
iconActive,
|
||||
}) => (
|
||||
<ConfigBox
|
||||
title={
|
||||
<Localized id="configure-general-reactions-title">
|
||||
@@ -54,6 +71,7 @@ const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
</Localized>
|
||||
<Localized id="configure-general-reactions-input">
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
className={styles.textInput}
|
||||
id={input.name}
|
||||
type="text"
|
||||
@@ -61,7 +79,6 @@ const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
placeholder="E.g. Respect"
|
||||
disabled={disabled}
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
</Localized>
|
||||
</FormField>
|
||||
@@ -74,9 +91,9 @@ const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
className={styles.reactionButton}
|
||||
reacted={false}
|
||||
label={input.value}
|
||||
labelActive={settings.reaction.labelActive}
|
||||
icon={settings.reaction.icon}
|
||||
iconActive={settings.reaction.iconActive}
|
||||
labelActive={input.value}
|
||||
icon={icon}
|
||||
iconActive={iconActive}
|
||||
totalReactions={0}
|
||||
onClick={() => null}
|
||||
/>
|
||||
@@ -93,6 +110,7 @@ const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
</Localized>
|
||||
<Localized id="configure-general-reactions-active-input">
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
className={styles.textInput}
|
||||
id={input.name}
|
||||
type="text"
|
||||
@@ -100,7 +118,6 @@ const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
fullWidth
|
||||
disabled={disabled}
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
</Localized>
|
||||
</FormField>
|
||||
@@ -112,10 +129,10 @@ const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
className={styles.reactionButton}
|
||||
readOnly
|
||||
reacted
|
||||
label={settings.reaction.label}
|
||||
label={input.value}
|
||||
labelActive={input.value}
|
||||
icon={settings.reaction.icon}
|
||||
iconActive={settings.reaction.iconActive}
|
||||
icon={icon}
|
||||
iconActive={iconActive}
|
||||
totalReactions={0}
|
||||
onClick={() => null}
|
||||
/>
|
||||
@@ -132,6 +149,7 @@ const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
</Localized>
|
||||
<Localized id="configure-general-reactions-sort-input">
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={input.name}
|
||||
className={styles.textInput}
|
||||
type="text"
|
||||
@@ -139,7 +157,6 @@ const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
fullWidth
|
||||
disabled={disabled}
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
</Localized>
|
||||
</FormField>
|
||||
|
||||
+12
-15
@@ -9,29 +9,26 @@ import ReactionConfig from "./ReactionConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class ReactionConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled, settings } = this.props;
|
||||
return <ReactionConfig settings={settings} disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
const ReactionConfigContainer: React.FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
settings,
|
||||
}) => {
|
||||
return (
|
||||
<ReactionConfig
|
||||
iconActive={settings.reaction.iconActive}
|
||||
icon={settings.reaction.icon}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment ReactionConfigContainer_settings on Settings {
|
||||
reaction {
|
||||
label
|
||||
labelActive
|
||||
sortLabel
|
||||
icon
|
||||
iconActive
|
||||
}
|
||||
|
||||
+12
-1
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent, Suspense } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { MarkdownEditor } from "coral-framework/components/loadables";
|
||||
import { parseEmptyAsNull } from "coral-framework/lib/form";
|
||||
@@ -19,6 +20,16 @@ import Header from "../../Header";
|
||||
import OnOffField from "../../OnOffField";
|
||||
import ValidationMessage from "../../ValidationMessage";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment SitewideCommentingConfig_formValues on Settings {
|
||||
disableCommenting {
|
||||
enabled
|
||||
message
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -82,8 +93,8 @@ const SitewideCommentingConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
<>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<MarkdownEditor
|
||||
id="configure-general-sitewideCommenting-message"
|
||||
{...input}
|
||||
id="configure-general-sitewideCommenting-message"
|
||||
/>
|
||||
</Suspense>
|
||||
<ValidationMessage meta={meta} fullWidth />
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { SitewideCommentingConfigContainer_settings as SettingsData } from "coral-admin/__generated__/SitewideCommentingConfigContainer_settings.graphql";
|
||||
|
||||
import SitewideCommentingConfig from "./SitewideCommentingConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class SitewideCommentingConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <SitewideCommentingConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment SitewideCommentingConfigContainer_settings on Settings {
|
||||
disableCommenting {
|
||||
enabled
|
||||
message
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(SitewideCommentingConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { required } from "coral-framework/lib/validation";
|
||||
import {
|
||||
@@ -13,20 +14,26 @@ import {
|
||||
Tag,
|
||||
} from "coral-ui/components/v2";
|
||||
|
||||
import { StaffConfigContainer_settings as SettingsData } from "coral-admin/__generated__/StaffConfigContainer_settings.graphql";
|
||||
|
||||
import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
import styles from "./StaffConfig.css";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment StaffConfig_formValues on Settings {
|
||||
staff {
|
||||
label
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
settings: SettingsData;
|
||||
}
|
||||
|
||||
const StaffConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
const StaffConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
<ConfigBox
|
||||
title={
|
||||
<Localized id="configure-general-staff-title">
|
||||
@@ -50,6 +57,7 @@ const StaffConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
</Localized>
|
||||
<Localized id="configure-general-staff-input">
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
className={styles.textInput}
|
||||
id={input.name}
|
||||
type="text"
|
||||
@@ -57,7 +65,6 @@ const StaffConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
|
||||
placeholder="E.g. Staff"
|
||||
disabled={disabled}
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
</Localized>
|
||||
</FormField>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { StaffConfigContainer_settings as SettingsData } from "coral-admin/__generated__/StaffConfigContainer_settings.graphql";
|
||||
|
||||
import StaffConfig from "./StaffConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class StaffConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled, settings } = this.props;
|
||||
return <StaffConfig settings={settings} disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment StaffConfigContainer_settings on Settings {
|
||||
staff {
|
||||
label
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(StaffConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -29,6 +29,7 @@ const APIKeyField: FunctionComponent<Props> = ({
|
||||
</Label>
|
||||
</Localized>
|
||||
<PasswordField
|
||||
{...input}
|
||||
id={`configure-moderation-${input.name}`}
|
||||
disabled={disabled}
|
||||
// TODO: (wyattjoh) figure out how to add translations to these props
|
||||
@@ -36,7 +37,6 @@ const APIKeyField: FunctionComponent<Props> = ({
|
||||
showPasswordTitle="Hide API Key"
|
||||
color={colorFromMeta(meta)}
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
|
||||
<ValidationMessage meta={meta} />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { parseEmptyAsNull } from "coral-framework/lib/form";
|
||||
import { ExternalLink } from "coral-framework/lib/i18n/components";
|
||||
@@ -25,6 +26,19 @@ import Subheader from "../../Subheader";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
import APIKeyField from "./APIKeyField";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment AkismetConfig_formValues on Settings {
|
||||
integrations {
|
||||
akismet {
|
||||
enabled
|
||||
key
|
||||
site
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -95,6 +109,7 @@ const AkismetConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id="configure-moderation-akismet-site"
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
@@ -103,7 +118,6 @@ const AkismetConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { AkismetConfigContainer_settings as SettingsData } from "coral-admin/__generated__/AkismetConfigContainer_settings.graphql";
|
||||
|
||||
import AkismetConfig from "./AkismetConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class AkismetConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <AkismetConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment AkismetConfigContainer_settings on Settings {
|
||||
integrations {
|
||||
akismet {
|
||||
enabled
|
||||
key
|
||||
site
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(AkismetConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,21 +0,0 @@
|
||||
import { noop } from "lodash";
|
||||
import React from "react";
|
||||
import { createRenderer } from "react-test-renderer/shallow";
|
||||
|
||||
import { removeFragmentRefs } from "coral-framework/testHelpers";
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
|
||||
import ModerationConfig from "./ModerationConfig";
|
||||
|
||||
const ModerationConfigN = removeFragmentRefs(ModerationConfig);
|
||||
|
||||
it("renders correctly", () => {
|
||||
const props: PropTypesOf<typeof ModerationConfigN> = {
|
||||
disabled: false,
|
||||
settings: {},
|
||||
onInitValues: noop,
|
||||
};
|
||||
const renderer = createRenderer();
|
||||
renderer.render(<ModerationConfigN {...props} />);
|
||||
expect(renderer.getRenderOutput()).toMatchSnapshot();
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import AkismetConfigContainer from "./AkismetConfigContainer";
|
||||
import PerspectiveConfigContainer from "./PerspectiveConfigContainer";
|
||||
import PreModerationConfigContainer from "./PreModerationConfigContainer";
|
||||
import RecentCommentHistoryConfigContainer from "./RecentCommentHistoryConfigContainer";
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
settings: PropTypesOf<typeof AkismetConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof PerspectiveConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof PreModerationConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof RecentCommentHistoryConfigContainer>["settings"];
|
||||
onInitValues: (values: any) => void;
|
||||
}
|
||||
|
||||
const ModerationConfig: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
settings,
|
||||
onInitValues,
|
||||
}) => (
|
||||
<HorizontalGutter size="double" data-testid="configure-moderationContainer">
|
||||
<PreModerationConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<RecentCommentHistoryConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<PerspectiveConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<AkismetConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
export default ModerationConfig;
|
||||
+30
-37
@@ -1,55 +1,48 @@
|
||||
import { FormApi } from "final-form";
|
||||
import { RouteProps } from "found";
|
||||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { useForm } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { pureMerge } from "coral-common/utils";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import {
|
||||
purgeMetadata,
|
||||
withFragmentContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { HorizontalGutter } from "coral-ui/components";
|
||||
|
||||
import { ModerationConfigContainer_settings as SettingsData } from "coral-admin/__generated__/ModerationConfigContainer_settings.graphql";
|
||||
|
||||
import ModerationConfig from "./ModerationConfig";
|
||||
import AkismetConfig from "./AkismetConfig";
|
||||
import PerspectiveConfig from "./PerspectiveConfig";
|
||||
import PreModerationConfig from "./PreModerationConfig";
|
||||
import RecentCommentHistoryConfig from "./RecentCommentHistoryConfig";
|
||||
|
||||
interface Props {
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
settings: SettingsData;
|
||||
}
|
||||
|
||||
class ModerationConfigContainer extends React.Component<Props> {
|
||||
public static routeConfig: RouteProps;
|
||||
private initialValues = {};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.props.form.initialize(this.initialValues);
|
||||
}
|
||||
|
||||
private handleOnInitValues = (values: any) => {
|
||||
this.initialValues = pureMerge(this.initialValues, values);
|
||||
};
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<ModerationConfig
|
||||
disabled={this.props.submitting}
|
||||
settings={this.props.settings}
|
||||
onInitValues={this.handleOnInitValues}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
export const ModerationConfigContainer: React.FunctionComponent<Props> = ({
|
||||
settings,
|
||||
submitting,
|
||||
}) => {
|
||||
const form = useForm();
|
||||
useMemo(() => form.initialize(purgeMetadata(settings)), []);
|
||||
return (
|
||||
<HorizontalGutter size="double" data-testid="configure-moderationContainer">
|
||||
<PreModerationConfig disabled={submitting} />
|
||||
<RecentCommentHistoryConfig disabled={submitting} />
|
||||
<PerspectiveConfig disabled={submitting} />
|
||||
<AkismetConfig disabled={submitting} />
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment ModerationConfigContainer_settings on Settings {
|
||||
...AkismetConfigContainer_settings
|
||||
...PerspectiveConfigContainer_settings
|
||||
...PreModerationConfigContainer_settings
|
||||
...RecentCommentHistoryConfigContainer_settings
|
||||
...AkismetConfig_formValues @relay(mask: false)
|
||||
...PerspectiveConfig_formValues @relay(mask: false)
|
||||
...PreModerationConfig_formValues @relay(mask: false)
|
||||
...RecentCommentHistoryConfig_formValues @relay(mask: false)
|
||||
}
|
||||
`,
|
||||
})(ModerationConfigContainer);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { FormApi } from "final-form";
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
@@ -11,7 +10,6 @@ import ModerationConfigContainer from "./ModerationConfigContainer";
|
||||
|
||||
interface Props {
|
||||
data: ModerationConfigRouteQueryResponse | null;
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
}
|
||||
|
||||
@@ -27,7 +25,6 @@ class ModerationConfigRoute extends React.Component<Props> {
|
||||
return (
|
||||
<ModerationConfigContainer
|
||||
settings={this.props.data.settings}
|
||||
form={this.props.form}
|
||||
submitting={this.props.submitting}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
TOXICITY_ENDPOINT_DEFAULT,
|
||||
@@ -34,13 +35,28 @@ import {
|
||||
import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import OnOffField from "../../OnOffField";
|
||||
import PermissionField from "../../PermissionField";
|
||||
import Subheader from "../../Subheader";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
import APIKeyField from "./APIKeyField";
|
||||
|
||||
import styles from "./PerspectiveConfig.css";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment PerspectiveConfig_formValues on Settings {
|
||||
integrations {
|
||||
perspective {
|
||||
enabled
|
||||
endpoint
|
||||
key
|
||||
model
|
||||
threshold
|
||||
doNotStore
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -108,6 +124,7 @@ const PerspectiveConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id="configure-moderation-perspective-threshold"
|
||||
classes={{
|
||||
input: styles.thresholdTextField,
|
||||
@@ -121,7 +138,6 @@ const PerspectiveConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
placeholder={TOXICITY_THRESHOLD_DEFAULT.toString()}
|
||||
textAlignCenter
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
@@ -154,6 +170,7 @@ const PerspectiveConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
<Field name="integrations.perspective.model" parse={parseEmptyAsNull}>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id="configure-moderation-perspective-model"
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
@@ -163,7 +180,6 @@ const PerspectiveConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
spellCheck={false}
|
||||
meta={meta}
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
@@ -181,9 +197,19 @@ const PerspectiveConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
</HelperText>
|
||||
</Localized>
|
||||
</FormFieldHeader>
|
||||
<PermissionField
|
||||
<OnOffField
|
||||
name="integrations.perspective.doNotStore"
|
||||
disabled={disabled}
|
||||
onLabel={
|
||||
<Localized id="configure-radioButton-allow">
|
||||
<span>Allow</span>
|
||||
</Localized>
|
||||
}
|
||||
offLabel={
|
||||
<Localized id="configure-radioButton-dontAllow">
|
||||
<span>Don't Allow</span>
|
||||
</Localized>
|
||||
}
|
||||
invert
|
||||
/>
|
||||
</FormField>
|
||||
@@ -229,6 +255,7 @@ const PerspectiveConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id="configure-moderation-perspective-customEndpoint"
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
@@ -238,7 +265,6 @@ const PerspectiveConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { PerspectiveConfigContainer_settings as SettingsData } from "coral-admin/__generated__/PerspectiveConfigContainer_settings.graphql";
|
||||
|
||||
import PerspectiveConfig from "./PerspectiveConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class PerspectiveConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <PerspectiveConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment PerspectiveConfigContainer_settings on Settings {
|
||||
integrations {
|
||||
perspective {
|
||||
enabled
|
||||
endpoint
|
||||
key
|
||||
model
|
||||
threshold
|
||||
doNotStore
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(PerspectiveConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { parseStringBool } from "coral-framework/lib/form";
|
||||
import { formatBool, parseStringBool } from "coral-framework/lib/form";
|
||||
import {
|
||||
FieldSet,
|
||||
FormField,
|
||||
@@ -13,6 +14,14 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import OnOffField from "../../OnOffField";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment PreModerationConfig_formValues on Settings {
|
||||
moderation
|
||||
premodLinksEnable
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -22,7 +31,7 @@ const parse = (v: string) => {
|
||||
};
|
||||
|
||||
const format = (v: "PRE" | "POST") => {
|
||||
return v === "PRE";
|
||||
return formatBool(v === "PRE");
|
||||
};
|
||||
|
||||
const PreModerationConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { PreModerationConfigContainer_settings as SettingsData } from "coral-admin/__generated__/PreModerationConfigContainer_settings.graphql";
|
||||
|
||||
import PreModerationConfig from "./PreModerationConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class PreModerationConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <PreModerationConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment PreModerationConfigContainer_settings on Settings {
|
||||
moderation
|
||||
premodLinksEnable
|
||||
}
|
||||
`,
|
||||
})(PreModerationConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
+13
-1
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { formatPercentage, parsePercentage } from "coral-framework/lib/form";
|
||||
import { hasError } from "coral-framework/lib/form/helpers";
|
||||
@@ -29,6 +30,17 @@ import ValidationMessage from "../../ValidationMessage";
|
||||
|
||||
import styles from "./RecentCommentHistoryConfig.css";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment RecentCommentHistoryConfig_formValues on Settings {
|
||||
recentCommentHistory {
|
||||
enabled
|
||||
timeFrame
|
||||
triggerRejectionRate
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -117,6 +129,7 @@ const RecentCommentHistoryConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
classes={{
|
||||
input: styles.thresholdTextField,
|
||||
}}
|
||||
@@ -128,7 +141,6 @@ const RecentCommentHistoryConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
adornment={<TextFieldAdornment>%</TextFieldAdornment>}
|
||||
meta={meta}
|
||||
textAlignCenter
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { RecentCommentHistoryConfigContainer_settings as SettingsData } from "coral-admin/__generated__/RecentCommentHistoryConfigContainer_settings.graphql";
|
||||
|
||||
import RecentCommentHistoryConfig from "./RecentCommentHistoryConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class RecentCommentHistoryConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <RecentCommentHistoryConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment RecentCommentHistoryConfigContainer_settings on Settings {
|
||||
recentCommentHistory {
|
||||
enabled
|
||||
timeFrame
|
||||
triggerRejectionRate
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(RecentCommentHistoryConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<ForwardRef(forwardRef)
|
||||
data-testid="configure-moderationContainer"
|
||||
size="double"
|
||||
>
|
||||
<Relay(PreModerationConfigContainer)
|
||||
disabled={false}
|
||||
onInitValues={[Function]}
|
||||
settings={Object {}}
|
||||
/>
|
||||
<Relay(RecentCommentHistoryConfigContainer)
|
||||
disabled={false}
|
||||
onInitValues={[Function]}
|
||||
settings={Object {}}
|
||||
/>
|
||||
<Relay(PerspectiveConfigContainer)
|
||||
disabled={false}
|
||||
onInitValues={[Function]}
|
||||
settings={Object {}}
|
||||
/>
|
||||
<Relay(AkismetConfigContainer)
|
||||
disabled={false}
|
||||
onInitValues={[Function]}
|
||||
settings={Object {}}
|
||||
/>
|
||||
</ForwardRef(forwardRef)>
|
||||
`;
|
||||
@@ -1,42 +0,0 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import OrganizationContactEmailConfigContainer from "./OrganizationContactEmailConfigContainer";
|
||||
import OrganizationNameConfigContainer from "./OrganizationNameConfigContainer";
|
||||
import OrganizationURLConfigContainer from "./OrganizationURLConfigContainer";
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
settings: PropTypesOf<typeof OrganizationNameConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof OrganizationContactEmailConfigContainer>["settings"] &
|
||||
PropTypesOf<typeof OrganizationURLConfigContainer>["settings"];
|
||||
onInitValues: (values: any) => void;
|
||||
}
|
||||
|
||||
const OrganizationConfig: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
settings,
|
||||
onInitValues,
|
||||
}) => (
|
||||
<HorizontalGutter size="double" data-testid="configure-organizationContainer">
|
||||
<OrganizationNameConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<OrganizationContactEmailConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<OrganizationURLConfigContainer
|
||||
disabled={disabled}
|
||||
settings={settings}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
export default OrganizationConfig;
|
||||
+30
-39
@@ -1,56 +1,47 @@
|
||||
import { FormApi } from "final-form";
|
||||
import { RouteProps } from "found";
|
||||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { useForm } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { pureMerge } from "coral-common/utils";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import {
|
||||
purgeMetadata,
|
||||
withFragmentContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { HorizontalGutter } from "coral-ui/components";
|
||||
|
||||
import { OrganizationConfigContainer_settings as SettingsData } from "coral-admin/__generated__/OrganizationConfigContainer_settings.graphql";
|
||||
|
||||
import OrganizationConfig from "./OrganizationConfig";
|
||||
import OrganizationContactEmailConfig from "./OrganizationContactEmailConfig";
|
||||
import OrganizationNameConfig from "./OrganizationNameConfig";
|
||||
import OrganizationURLConfig from "./OrganizationURLConfig";
|
||||
|
||||
interface Props {
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
settings: SettingsData;
|
||||
}
|
||||
|
||||
class OrganizationConfigContainer extends React.Component<Props> {
|
||||
public static routeConfig: RouteProps;
|
||||
private initialValues = {};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.props.form.initialize(this.initialValues);
|
||||
}
|
||||
|
||||
private handleOnInitValues = (values: any) => {
|
||||
this.initialValues = pureMerge(this.initialValues, values);
|
||||
};
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<OrganizationConfig
|
||||
disabled={this.props.submitting}
|
||||
settings={this.props.settings}
|
||||
onInitValues={this.handleOnInitValues}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const OrganizationConfigContainer: React.FunctionComponent<Props> = ({
|
||||
settings,
|
||||
submitting,
|
||||
}) => {
|
||||
const form = useForm();
|
||||
useMemo(() => form.initialize(purgeMetadata(settings)), []);
|
||||
return (
|
||||
<HorizontalGutter
|
||||
size="double"
|
||||
data-testid="configure-organizationContainer"
|
||||
>
|
||||
<OrganizationNameConfig disabled={submitting} />
|
||||
<OrganizationContactEmailConfig disabled={submitting} />
|
||||
<OrganizationURLConfig disabled={submitting} />
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment OrganizationConfigContainer_settings on Settings {
|
||||
...OrganizationNameConfigContainer_settings
|
||||
...OrganizationContactEmailConfigContainer_settings
|
||||
...OrganizationURLConfigContainer_settings
|
||||
...OrganizationNameConfig_formValues @relay(mask: false)
|
||||
...OrganizationContactEmailConfig_formValues @relay(mask: false)
|
||||
...OrganizationURLConfig_formValues @relay(mask: false)
|
||||
}
|
||||
`,
|
||||
})(OrganizationConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
|
||||
-3
@@ -1,4 +1,3 @@
|
||||
import { FormApi } from "final-form";
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
@@ -11,7 +10,6 @@ import OrganizationConfigContainer from "./OrganizationConfigContainer";
|
||||
|
||||
interface Props {
|
||||
data: OrganizationConfigRouteQueryResponse | null;
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
}
|
||||
|
||||
@@ -27,7 +25,6 @@ class OrganizationConfigRoute extends React.Component<Props> {
|
||||
return (
|
||||
<OrganizationConfigContainer
|
||||
settings={this.props.data.settings}
|
||||
form={this.props.form}
|
||||
submitting={this.props.submitting}
|
||||
/>
|
||||
);
|
||||
|
||||
+15
-3
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
composeValidators,
|
||||
@@ -13,11 +14,22 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment OrganizationContactEmailConfig_formValues on Settings {
|
||||
organization {
|
||||
contactEmail
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const OrganizationNameConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
const OrganizationContactEmailConfig: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
}) => (
|
||||
<ConfigBox
|
||||
title={
|
||||
<Localized id="configure-organization-email">
|
||||
@@ -41,6 +53,7 @@ const OrganizationNameConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={`configure-organization-${input.name}`}
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
@@ -49,11 +62,10 @@ const OrganizationNameConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</ConfigBox>
|
||||
);
|
||||
|
||||
export default OrganizationNameConfig;
|
||||
export default OrganizationContactEmailConfig;
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { OrganizationContactEmailConfigContainer_settings as SettingsData } from "coral-admin/__generated__/OrganizationContactEmailConfigContainer_settings.graphql";
|
||||
|
||||
import OrganizationContactEmailConfig from "./OrganizationContactEmailConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class OrganizationContactEmailConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <OrganizationContactEmailConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment OrganizationContactEmailConfigContainer_settings on Settings {
|
||||
organization {
|
||||
contactEmail
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(OrganizationContactEmailConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
+11
-1
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { required } from "coral-framework/lib/validation";
|
||||
import { FormFieldDescription } from "coral-ui/components/v2";
|
||||
@@ -9,6 +10,15 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment OrganizationNameConfig_formValues on Settings {
|
||||
organization {
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -32,6 +42,7 @@ const OrganizationNameConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
<Field name="organization.name" validate={required}>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={`configure-organization-${input.name}`}
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
@@ -40,7 +51,6 @@ const OrganizationNameConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
spellCheck={false}
|
||||
meta={meta}
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { OrganizationNameConfigContainer_settings as SettingsData } from "coral-admin/__generated__/OrganizationNameConfigContainer_settings.graphql";
|
||||
|
||||
import OrganizationNameConfig from "./OrganizationNameConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class OrganizationNameConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <OrganizationNameConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment OrganizationNameConfigContainer_settings on Settings {
|
||||
organization {
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(OrganizationNameConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
+11
-1
@@ -1,6 +1,7 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
composeValidators,
|
||||
@@ -13,6 +14,15 @@ import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import TextFieldWithValidation from "../../TextFieldWithValidation";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment OrganizationURLConfig_formValues on Settings {
|
||||
organization {
|
||||
url
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -36,6 +46,7 @@ const OrganizationURLConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<TextFieldWithValidation
|
||||
{...input}
|
||||
id={`configure-organization-${input.name}`}
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
@@ -44,7 +55,6 @@ const OrganizationURLConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
meta={meta}
|
||||
{...input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { OrganizationURLConfigContainer_settings as SettingsData } from "coral-admin/__generated__/OrganizationURLConfigContainer_settings.graphql";
|
||||
|
||||
import OrganizationURLConfig from "./OrganizationURLConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class OrganizationURLConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <OrganizationURLConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment OrganizationURLConfigContainer_settings on Settings {
|
||||
organization {
|
||||
url
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(OrganizationURLConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
FormField,
|
||||
@@ -15,6 +16,15 @@ import WordListTextArea from "./WordListTextArea";
|
||||
|
||||
import styles from "./BannedWordListConfig.css";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment BannedWordListConfig_formValues on Settings {
|
||||
wordList {
|
||||
banned
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { BannedWordListConfigContainer_settings as SettingsData } from "coral-admin/__generated__/BannedWordListConfigContainer_settings.graphql";
|
||||
|
||||
import BannedWordListConfig from "./BannedWordListConfig";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
onInitValues: (values: SettingsData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
class BannedWordListConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues(props.settings);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <BannedWordListConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment BannedWordListConfigContainer_settings on Settings {
|
||||
wordList {
|
||||
banned
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(BannedWordListConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
FormField,
|
||||
@@ -15,6 +16,15 @@ import WordListTextArea from "./WordListTextArea";
|
||||
|
||||
import styles from "./SuspectWordListConfig.css";
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment SuspectWordListConfig_formValues on Settings {
|
||||
wordList {
|
||||
suspect
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user