From 53e168ae9397b98fe3163e3074705a68e081a78e Mon Sep 17 00:00:00 2001 From: Kiwi Date: Thu, 7 Feb 2019 01:10:51 +0100 Subject: [PATCH] [next] Implement configuration panes (#2173) * feat: moderation config * feat: configure banned and suspect words * chore: upgrade react and test libs to the newest version <3 * chore: upgrade typescript + some refactor * feat: general, organization and advanced configuration panes * fix: translation * feat: speedup fetching markdown editor * feat: localize markdown editor * chore: refactor container names * chore: rename infobox to communityGuidelines * feat: closing comment streams duration config * test: add feature tests for configurations * fix: mock only console.error * chore: upgrade node * chore: require node >= 10 * fix: better validation and default values * feat: Make DurationField a general purpose component and reuse for Edit Comment Timeframe * test: add unit test for duration field * fix: patch for bug when built in production * chore: bump npm version to latest * fix: adapted Dockerfile to new version of node * refactor: harmonized seconds/milliseconds to seconds * fix: resolve bug from merge conflict --- .circleci/config.yml | 4 +- Dockerfile | 4 +- package-lock.json | 790 +++-------- package.json | 36 +- src/core/build/publicPath.js | 5 +- .../components/DecisionHistoryButton.tsx | 2 +- .../__snapshots__/AuthBox.spec.tsx.snap | 16 +- .../__snapshots__/SignOutButton.spec.tsx.snap | 4 +- .../admin/mutations/UpdateSettingsMutation.ts | 15 +- src/core/client/admin/routeConfig.tsx | 31 +- .../__snapshots__/Community.spec.tsx.snap | 4 +- .../components/ConfigurationSubHeader.tsx | 12 + .../routes/configure/components/Configure.tsx | 20 +- .../routes/configure/components/Header.css | 2 + .../routes/configure/components/Header.tsx | 17 +- .../configure/components/Moderation.tsx | 19 - .../configure/components/Navigation/Link.css | 12 +- .../configure/components/OnOffField.tsx | 61 + .../configure/components/PermissionField.tsx | 61 + .../routes/configure/components/Subheader.css | 5 + ...Moderation.spec.tsx => Subheader.spec.tsx} | 6 +- .../routes/configure/components/Subheader.tsx | 12 + .../components/ValidationMessage.css | 0 .../components/ValidationMessage.tsx | 21 + .../__snapshots__/ConfigBox.spec.tsx.snap | 4 +- .../__snapshots__/Header.spec.tsx.snap | 4 +- .../__snapshots__/Layout.spec.tsx.snap | 4 +- .../__snapshots__/Moderation.spec.tsx.snap | 12 - .../__snapshots__/Subheader.spec.tsx.snap | 10 + .../containers/ConfigureContainer.tsx | 2 +- .../advanced/components/AdvancedConfig.tsx | 35 + .../advanced/components/CustomCSSConfig.tsx | 64 + .../components/PermittedDomainsConfig.tsx | 65 + .../containers/AdvancedConfigContainer.tsx | 54 + .../AdvancedConfigRouteContainer.tsx | 47 + .../containers/CustomCSSConfigContainer.tsx | 35 + .../PermittedDomainsConfigContainer.tsx | 35 + .../components/{Auth.tsx => AuthConfig.tsx} | 8 +- .../auth/components/ClientIDField.tsx | 2 +- .../auth/components/ClientSecretField.tsx | 2 +- .../auth/components/DisplayNamesConfig.tsx | 7 +- .../sections/auth/components/OIDCConfig.tsx | 3 +- .../auth/components/ValidationMessage.tsx | 15 - ...hContainer.tsx => AuthConfigContainer.tsx} | 12 +- ...ainer.tsx => AuthConfigRouteContainer.tsx} | 12 +- .../components/ClosedStreamMessageConfig.tsx | 61 + .../ClosingCommentStreamsConfig.tsx | 87 ++ .../components/CommentEditingConfig.css | 3 + .../components/CommentEditingConfig.tsx | 78 ++ .../components/CommentLengthConfig.css | 3 + .../components/CommentLengthConfig.tsx | 163 +++ .../general/components/GeneralConfig.tsx | 56 + .../general/components/GuidelinesConfig.tsx | 81 ++ .../general/components/LazyMarkdown.ts | 7 + .../ClosedStreamMessageConfigContainer.tsx | 35 + .../ClosingCommentStreamsConfigContainer.tsx | 36 + .../CommentEditingConfigContainer.tsx | 35 + .../CommentLengthConfigContainer.tsx | 39 + .../containers/GeneralConfigContainer.tsx | 57 + .../GeneralConfigRouteContainer.tsx | 54 + .../containers/GuidelinesConfigContainer.tsx | 36 + .../moderation/components/APIKeyField.tsx | 54 + .../moderation/components/AkismetConfig.tsx | 123 ++ .../components/ModerationConfig.spec.tsx | 21 + .../components/ModerationConfig.tsx | 35 + .../components/PerspectiveConfig.css | 3 + .../components/PerspectiveConfig.tsx | 213 +++ .../ModerationConfig.spec.tsx.snap | 19 + .../containers/AkismetConfigContainer.tsx | 41 + .../containers/ModerationConfigContainer.tsx | 54 + .../ModerationConfigRouteContainer.tsx | 47 + .../containers/PerspectiveConfigContainer.tsx | 43 + .../components/OrganizationConfig.tsx | 35 + .../OrganizationContactEmailConfig.tsx | 66 + .../components/OrganizationNameConfig.tsx | 69 + .../OrganizationConfigContainer.tsx | 54 + ...rganizationContactEmailConfigContainer.tsx | 35 + .../OrganizationNameConfigContainer.tsx | 35 + .../containers/OrganizationRouteContainer.tsx | 47 + .../components/BannedWordListConfig.css | 3 + .../components/BannedWordListConfig.tsx | 63 + .../components/SuspectWordListConfig.css | 3 + .../components/SuspectWordListConfig.tsx | 64 + .../wordList/components/WordListConfig.tsx | 35 + .../wordList/components/WordListTextArea.css | 6 + .../wordList/components/WordListTextArea.tsx | 61 + .../BannedWordListConfigContainer.tsx | 37 + .../SuspectWordListConfigContainer.tsx | 37 + .../containers/WordListConfigContainer.tsx | 54 + .../containers/WordListRouteContainer.tsx | 47 + .../CompleteAccountBox.spec.tsx.snap | 12 +- .../__snapshots__/UnorderedList.spec.tsx.snap | 4 +- .../HorizontalSeparator.spec.tsx.snap | 4 +- .../__snapshots__/SignIn.spec.tsx.snap | 6 +- .../__snapshots__/AcceptButton.spec.tsx.snap | 16 +- .../CommentContent.spec.tsx.snap | 4 +- .../__snapshots__/InReplyTo.spec.tsx.snap | 12 +- .../__snapshots__/ModerateCard.spec.tsx.snap | 140 +- .../__snapshots__/Navigation.spec.tsx.snap | 32 +- .../__snapshots__/Queue.spec.tsx.snap | 12 +- .../__snapshots__/RejectButton.spec.tsx.snap | 16 +- .../__snapshots__/Timestamp.spec.tsx.snap | 2 +- .../__snapshots__/Stories.spec.tsx.snap | 4 +- .../addEmailAddress.spec.tsx.snap | 336 +++-- .../createUsername.spec.tsx.snap | 144 +- .../signInWithEmail.spec.tsx.snap | 144 +- .../__snapshots__/advanced.spec.tsx.snap | 191 +++ .../__snapshots__/auth.spec.tsx.snap | 1238 ++++++++++------- .../__snapshots__/general.spec.tsx.snap | 604 ++++++++ .../__snapshots__/moderation.spec.tsx.snap | 536 +++++++ .../__snapshots__/organization.spec.tsx.snap | 192 +++ .../__snapshots__/wordList.spec.tsx.snap | 229 +++ .../admin/test/configure/advanced.spec.tsx | 211 +++ .../admin/test/configure/general.spec.tsx | 406 ++++++ .../admin/test/configure/moderation.spec.tsx | 293 ++++ .../test/configure/organization.spec.tsx | 197 +++ .../admin/test/configure/wordList.spec.tsx | 125 ++ src/core/client/admin/test/fixtures.ts | 27 +- .../AcceptedComment.spec.tsx.snap | 4 +- .../__snapshots__/AcceptedIcon.spec.tsx.snap | 4 +- .../DecisionHistoryLoading.spec.tsx.snap | 4 +- .../__snapshots__/DecisionItem.spec.tsx.snap | 4 +- .../__snapshots__/Empty.spec.tsx.snap | 8 +- .../__snapshots__/Footer.spec.tsx.snap | 4 +- .../GoToCommentLink.spec.tsx.snap | 4 +- .../__snapshots__/Info.spec.tsx.snap | 4 +- .../RejectedComment.spec.tsx.snap | 4 +- .../__snapshots__/RejectedIcon.spec.tsx.snap | 4 +- .../ShowMoreButton.spec.tsx.snap | 4 +- .../__snapshots__/Timestamp.spec.tsx.snap | 2 +- .../__snapshots__/Title.spec.tsx.snap | 12 +- .../__snapshots__/Restricted.spec.tsx.snap | 32 +- .../Header/__snapshots__/Bar.spec.tsx.snap | 4 +- .../Header/__snapshots__/SubBar.spec.tsx.snap | 4 +- .../__snapshots__/Subtitle.spec.tsx.snap | 4 +- .../Header/__snapshots__/Title.spec.tsx.snap | 4 +- .../HorizontalSeparator.spec.tsx.snap | 4 +- .../addEmailAddress.spec.tsx.snap | 336 +++-- .../createUsername.spec.tsx.snap | 144 +- .../test/__snapshots__/signIn.spec.tsx.snap | 216 +-- .../test/__snapshots__/signUp.spec.tsx.snap | 624 +++++---- .../__snapshots__/UnorderedList.spec.tsx.snap | 4 +- .../__snapshots__/SignIn.spec.tsx.snap | 63 +- .../__snapshots__/SignUp.spec.tsx.snap | 42 +- src/core/client/embed/index.spec.ts | 4 +- .../framework/components/DurationField.css | 8 + .../components/DurationField.spec.tsx | 71 + .../framework/components/DurationField.tsx | 223 +++ .../__snapshots__/DurationField.spec.tsx.snap | 306 ++++ .../FacebookButton.spec.tsx.snap | 4 +- .../__snapshots__/GoogleButton.spec.tsx.snap | 4 +- .../__snapshots__/OIDCButton.spec.tsx.snap | 4 +- src/core/client/framework/components/index.ts | 1 + .../components/loadables/MarkdownEditor.css | 631 +++++++++ .../components/loadables/MarkdownEditor.tsx | 215 +++ src/core/client/framework/lib/form.tsx | 54 + .../lib/i18n/components/ExternalLink.css | 3 + .../lib/i18n/components/ExternalLink.spec.tsx | 15 + .../lib/i18n/components/ExternalLink.tsx | 14 + .../__snapshots__/ExternalLink.spec.tsx.snap | 11 + .../framework/lib/i18n/components/index.ts | 1 + src/core/client/framework/lib/i18n/index.ts | 1 + .../framework/lib/i18n/withGetMessage.tsx | 42 + src/core/client/framework/lib/messages.tsx | 28 + .../framework/lib/router/withRouteConfig.ts | 2 +- src/core/client/framework/lib/validation.tsx | 62 +- .../framework/testHelpers/inputPredicate.ts | 2 +- .../__snapshots__/App.spec.tsx.snap | 4 +- .../__snapshots__/Timestamp.spec.tsx.snap | 2 +- .../UserBoxAuthenticated.spec.tsx.snap | 29 +- .../UserBoxUnauthenticated.spec.tsx.snap | 36 +- .../__snapshots__/ButtonsBar.spec.tsx.snap | 4 +- .../__snapshots__/Comment.spec.tsx.snap | 12 +- .../__snapshots__/InReplyTo.spec.tsx.snap | 12 +- .../__snapshots__/ReplyButton.spec.tsx.snap | 8 +- .../ShowConversationLink.spec.tsx.snap | 4 +- .../PermalinkButton/PermalinkButton.tsx | 2 +- .../components/ReportButton/ReportButton.tsx | 17 +- .../ReportButton/ReportButtonWithPopover.tsx | 2 +- .../__snapshots__/ReportButton.spec.tsx.snap | 55 +- .../PostCommentFormFake.spec.tsx.snap | 8 +- .../__snapshots__/PoweredBy.spec.tsx.snap | 6 +- .../__snapshots__/RTE.spec.tsx.snap | 12 +- .../__snapshots__/ReplyList.spec.tsx.snap | 44 +- .../__snapshots__/ReplyTo.spec.tsx.snap | 12 +- .../__snapshots__/Stream.spec.tsx.snap | 88 +- .../CommentContainer.spec.tsx.snap | 40 +- .../ConversationThread.spec.tsx.snap | 32 +- .../__snapshots__/PermalinkView.spec.tsx.snap | 48 +- .../__snapshots__/ReportPopover.spec.tsx.snap | 8 +- .../__snapshots__/ThankYou.spec.tsx.snap | 8 +- .../CommentHistory.spec.tsx.snap | 32 +- .../HistoryComment.spec.tsx.snap | 28 +- .../__snapshots__/Profile.spec.tsx.snap | 4 +- src/core/client/test/mocks.ts | 1 + .../ui/components/AriaInfo/AriaInfo.tsx | 10 +- .../ui/components/BaseButton/BaseButton.tsx | 9 +- .../client/ui/components/Button/Button.tsx | 6 +- .../ui/components/Button/ButtonIcon.tsx | 2 +- .../Button/__snapshots__/Button.spec.tsx.snap | 17 +- .../__snapshots__/ButtonIcon.spec.tsx.snap | 9 +- .../ui/components/FormField/FormField.tsx | 3 +- .../__snapshots__/FormField.spec.tsx.snap | 14 +- .../HorizontalGutter/HorizontalGutter.css | 3 + .../ui/components/InputLabel/InputLabel.tsx | 6 +- .../ui/components/Message/MessageIcon.tsx | 2 +- .../ui/components/RadioButton/RadioButton.css | 4 +- .../ui/components/SelectField/SelectField.css | 2 +- .../ui/components/TextField/TextField.css | 16 +- .../ui/components/TextField/TextField.mdx | 17 +- .../components/TextField/TextField.spec.tsx | 1 + .../ui/components/TextField/TextField.tsx | 28 +- .../__snapshots__/TextField.spec.tsx.snap | 17 +- src/core/client/ui/hocs/withForwardRef.tsx | 26 +- src/core/client/ui/shared/typography.css | 18 + .../server/graph/tenant/mutators/Settings.ts | 4 +- .../server/graph/tenant/resolvers/Comment.ts | 6 +- .../server/graph/tenant/resolvers/Story.ts | 17 +- .../server/graph/tenant/schema/schema.graphql | 31 +- src/core/server/models/settings.ts | 13 +- src/core/server/models/tenant.ts | 23 +- src/core/server/services/comments/index.ts | 48 +- .../comments/pipeline/phases/storyClosed.ts | 14 +- src/core/server/services/stories/index.ts | 27 + src/locales/de/framework.ftl | 3 +- src/locales/en-US/admin.ftl | 144 +- src/locales/en-US/framework.ftl | 45 +- src/locales/es/framework.ftl | 1 + 228 files changed, 10582 insertions(+), 2774 deletions(-) create mode 100644 src/core/client/admin/routes/configure/components/ConfigurationSubHeader.tsx delete mode 100644 src/core/client/admin/routes/configure/components/Moderation.tsx create mode 100644 src/core/client/admin/routes/configure/components/OnOffField.tsx create mode 100644 src/core/client/admin/routes/configure/components/PermissionField.tsx create mode 100644 src/core/client/admin/routes/configure/components/Subheader.css rename src/core/client/admin/routes/configure/components/{Moderation.spec.tsx => Subheader.spec.tsx} (69%) create mode 100644 src/core/client/admin/routes/configure/components/Subheader.tsx rename src/core/client/admin/routes/configure/{sections/auth => }/components/ValidationMessage.css (100%) create mode 100644 src/core/client/admin/routes/configure/components/ValidationMessage.tsx delete mode 100644 src/core/client/admin/routes/configure/components/__snapshots__/Moderation.spec.tsx.snap create mode 100644 src/core/client/admin/routes/configure/components/__snapshots__/Subheader.spec.tsx.snap create mode 100644 src/core/client/admin/routes/configure/sections/advanced/components/AdvancedConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/advanced/components/CustomCSSConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/advanced/components/PermittedDomainsConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/advanced/containers/AdvancedConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/advanced/containers/AdvancedConfigRouteContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/advanced/containers/CustomCSSConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/advanced/containers/PermittedDomainsConfigContainer.tsx rename src/core/client/admin/routes/configure/sections/auth/components/{Auth.tsx => AuthConfig.tsx} (87%) delete mode 100644 src/core/client/admin/routes/configure/sections/auth/components/ValidationMessage.tsx rename src/core/client/admin/routes/configure/sections/auth/containers/{AuthContainer.tsx => AuthConfigContainer.tsx} (91%) rename src/core/client/admin/routes/configure/sections/auth/containers/{AuthRouteContainer.tsx => AuthConfigRouteContainer.tsx} (70%) create mode 100644 src/core/client/admin/routes/configure/sections/general/components/ClosedStreamMessageConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/components/ClosingCommentStreamsConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/components/CommentEditingConfig.css create mode 100644 src/core/client/admin/routes/configure/sections/general/components/CommentEditingConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/components/CommentLengthConfig.css create mode 100644 src/core/client/admin/routes/configure/sections/general/components/CommentLengthConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/components/GeneralConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/components/GuidelinesConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/components/LazyMarkdown.ts create mode 100644 src/core/client/admin/routes/configure/sections/general/containers/ClosedStreamMessageConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/containers/ClosingCommentStreamsConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/containers/CommentEditingConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/containers/CommentLengthConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/containers/GeneralConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/containers/GeneralConfigRouteContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/general/containers/GuidelinesConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/moderation/components/APIKeyField.tsx create mode 100644 src/core/client/admin/routes/configure/sections/moderation/components/AkismetConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/moderation/components/ModerationConfig.spec.tsx create mode 100644 src/core/client/admin/routes/configure/sections/moderation/components/ModerationConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/moderation/components/PerspectiveConfig.css create mode 100644 src/core/client/admin/routes/configure/sections/moderation/components/PerspectiveConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/moderation/components/__snapshots__/ModerationConfig.spec.tsx.snap create mode 100644 src/core/client/admin/routes/configure/sections/moderation/containers/AkismetConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/moderation/containers/ModerationConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/moderation/containers/ModerationConfigRouteContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/moderation/containers/PerspectiveConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/organization/components/OrganizationConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/organization/components/OrganizationContactEmailConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/organization/components/OrganizationNameConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/organization/containers/OrganizationConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/organization/containers/OrganizationContactEmailConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/organization/containers/OrganizationNameConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/organization/containers/OrganizationRouteContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/wordList/components/BannedWordListConfig.css create mode 100644 src/core/client/admin/routes/configure/sections/wordList/components/BannedWordListConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/wordList/components/SuspectWordListConfig.css create mode 100644 src/core/client/admin/routes/configure/sections/wordList/components/SuspectWordListConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/wordList/components/WordListConfig.tsx create mode 100644 src/core/client/admin/routes/configure/sections/wordList/components/WordListTextArea.css create mode 100644 src/core/client/admin/routes/configure/sections/wordList/components/WordListTextArea.tsx create mode 100644 src/core/client/admin/routes/configure/sections/wordList/containers/BannedWordListConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/wordList/containers/SuspectWordListConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/wordList/containers/WordListConfigContainer.tsx create mode 100644 src/core/client/admin/routes/configure/sections/wordList/containers/WordListRouteContainer.tsx create mode 100644 src/core/client/admin/test/configure/__snapshots__/advanced.spec.tsx.snap create mode 100644 src/core/client/admin/test/configure/__snapshots__/general.spec.tsx.snap create mode 100644 src/core/client/admin/test/configure/__snapshots__/moderation.spec.tsx.snap create mode 100644 src/core/client/admin/test/configure/__snapshots__/organization.spec.tsx.snap create mode 100644 src/core/client/admin/test/configure/__snapshots__/wordList.spec.tsx.snap create mode 100644 src/core/client/admin/test/configure/advanced.spec.tsx create mode 100644 src/core/client/admin/test/configure/general.spec.tsx create mode 100644 src/core/client/admin/test/configure/moderation.spec.tsx create mode 100644 src/core/client/admin/test/configure/organization.spec.tsx create mode 100644 src/core/client/admin/test/configure/wordList.spec.tsx create mode 100644 src/core/client/framework/components/DurationField.css create mode 100644 src/core/client/framework/components/DurationField.spec.tsx create mode 100644 src/core/client/framework/components/DurationField.tsx create mode 100644 src/core/client/framework/components/__snapshots__/DurationField.spec.tsx.snap create mode 100644 src/core/client/framework/components/loadables/MarkdownEditor.css create mode 100644 src/core/client/framework/components/loadables/MarkdownEditor.tsx create mode 100644 src/core/client/framework/lib/i18n/components/ExternalLink.css create mode 100644 src/core/client/framework/lib/i18n/components/ExternalLink.spec.tsx create mode 100644 src/core/client/framework/lib/i18n/components/ExternalLink.tsx create mode 100644 src/core/client/framework/lib/i18n/components/__snapshots__/ExternalLink.spec.tsx.snap create mode 100644 src/core/client/framework/lib/i18n/components/index.ts create mode 100644 src/core/client/framework/lib/i18n/withGetMessage.tsx diff --git a/.circleci/config.yml b/.circleci/config.yml index 807110415..0f9c60369 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ job_environment: &job_environment job_defaults: &job_defaults working_directory: ~/coralproject/talk docker: - - image: circleci/node:8 + - image: circleci/node:10 environment: <<: *job_environment @@ -103,7 +103,7 @@ jobs: command: npm run build - run: name: Verify Bundle Size - command: npm run bundlesize + command: npx bundlesize - save_cache: key: v1-build-cache-{{ .Branch }}-{{ .Revision }} paths: diff --git a/Dockerfile b/Dockerfile index e37c6dada..9813db2af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:8-alpine +FROM node:10-alpine # Install build dependancies. RUN apk --no-cache add git @@ -19,7 +19,7 @@ RUN NODE_ENV=development npm install && \ npm run build && \ npm prune --production -FROM node:8-alpine +FROM node:10-alpine # Create app directory RUN mkdir -p /usr/src/app diff --git a/package-lock.json b/package-lock.json index d5dd01fc5..be784064a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2175,9 +2175,9 @@ } }, "@types/enzyme": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.1.11.tgz", - "integrity": "sha512-abPTpLuveNVd2ibafCjwoZT9MerzgnBKd6ijNKdtlfJREGKXk5dxzKAXGoY9IuiYarH2YXTc197WeyIVQUFjQg==", + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.1.15.tgz", + "integrity": "sha512-6b4JWgV+FNec1c4+8HauGbXg5gRc1oQK93t2+4W+bHjG/PzO+iPvagY6d6bXAZ+t+ps51Zb2F9LQ4vl0S0Epog==", "dev": true, "requires": { "@types/cheerio": "*", @@ -2185,9 +2185,9 @@ } }, "@types/enzyme-adapter-react-16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.2.tgz", - "integrity": "sha512-/oEtlwJyFIT9metXC2A90XnjfHwBDYxhFoJwqNjNDG5K2CCqp7xneQbAp4u5j280bOmalFYUDjfmmxNQG3S4Og==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.3.tgz", + "integrity": "sha512-9eRLBsC/Djkys05BdTWgav8v6fSCjyzjNuLwG2sfa2b2g/VAN10luP0zB0VwtOWFQ0LGjIboJJvIsVdU5gqRmg==", "dev": true, "requires": { "@types/enzyme": "*" @@ -2508,13 +2508,10 @@ } }, "@types/prop-types": { - "version": "15.5.4", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.4.tgz", - "integrity": "sha512-RnC6YeQDmDas7DToCbRWNntB9XpIR+sqg1zUqcCUxOJTBwGeSAPfTQaXqzyNND82FIBNY67r17FedDyaKRcHBQ==", - "dev": true, - "requires": { - "@types/react": "*" - } + "version": "15.5.8", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz", + "integrity": "sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw==", + "dev": true }, "@types/q": { "version": "1.5.1", @@ -2528,11 +2525,12 @@ "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" }, "@types/react": { - "version": "16.4.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.2.tgz", - "integrity": "sha512-oVcVteCDNiVc/fkDjowRfAZQDEOR76j3CJ3FvwXNvfV6zJguhghy1lMgpAzYox+9AZsWch+JPV6Imml3wvIUeg==", + "version": "16.7.20", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.7.20.tgz", + "integrity": "sha512-Qd5RWkwl6SL7R2XzLk/cicjVQm1Mhc6HqXY5Ei4pWd1Vi8Fkbd5O0sA398x8fRSTPAuHdDYD9nrWmJMYTJI0vQ==", "dev": true, "requires": { + "@types/prop-types": "*", "csstype": "^2.2.0" } }, @@ -2546,12 +2544,11 @@ } }, "@types/react-dom": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.0.6.tgz", - "integrity": "sha512-M+1zmwa5KxUpkCuxA4whlDJKYTGNvNQW4pIoCLH16xGbClicD9CzPry4y94kTjCCk/bJZCZ/GVqUsP7eKcO/mQ==", + "version": "16.0.11", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.0.11.tgz", + "integrity": "sha512-x6zUx9/42B5Kl2Vl9HlopV8JF64wLpX3c+Pst9kc1HgzrsH+mkehe/zmHMQTplIrR48H2gpU7ZqurQolYu8XBA==", "dev": true, "requires": { - "@types/node": "*", "@types/react": "*" } }, @@ -2575,9 +2572,9 @@ } }, "@types/react-test-renderer": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.0.1.tgz", - "integrity": "sha512-kmNh8g67Ck/y/vp6KX+4JTJXiTGLZBylNhu+R7sm7zcvsrnIGVO6J1zew5inVg428j9f8yHpl68RcYOZXVborQ==", + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.0.3.tgz", + "integrity": "sha512-NWOAxVQeJxpXuNKgw83Hah0nquiw1nUexM9qY/Hk3a+XhZwgMtaa6GLA9E1TKMT75Odb3/KE/jiBO4enTuEJjQ==", "dev": true, "requires": { "@types/react": "*" @@ -2631,6 +2628,12 @@ "@types/mime": "*" } }, + "@types/simplemde": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@types/simplemde/-/simplemde-1.11.7.tgz", + "integrity": "sha512-b3yirBar1gqb9clgJJKpx+Or3txkOOkm5sffhRKaBUfh7+1D2Aimx2RKXGS1LK9S5KyNmug3lwpGJtaID9cOnA==", + "dev": true + }, "@types/sinon": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-5.0.1.tgz", @@ -3490,16 +3493,6 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3898,16 +3891,6 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" }, - "axios": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz", - "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=", - "dev": true, - "requires": { - "follow-redirects": "^1.2.5", - "is-buffer": "^1.1.5" - } - }, "b3b": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/b3b/-/b3b-0.0.1.tgz", @@ -5911,25 +5894,6 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=" }, - "bl": { - "version": "1.2.2", - "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", - "dev": true, - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "~2.0.0" - } - }, "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", @@ -6094,16 +6058,6 @@ "base64-js": "^1.1.2" } }, - "brotli-size": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-0.0.1.tgz", - "integrity": "sha1-jBruoBzSLzWbBIlRGFvVOf8Mgp8=", - "dev": true, - "requires": { - "duplexer": "^0.1.1", - "iltorb": "^1.0.9" - } - }, "browser-process-hrtime": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", @@ -6232,22 +6186,6 @@ "isarray": "^1.0.0" } }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, "buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", @@ -6259,12 +6197,6 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true - }, "buffer-from": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", @@ -6308,50 +6240,6 @@ "uuid": "^3.2.1" } }, - "bundlesize": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/bundlesize/-/bundlesize-0.17.0.tgz", - "integrity": "sha512-w/jYWQupw/eijqx4LV6PPr1z0HmfXT8SMCg9tls1a3xrr6/PtC22MoFrYJ8j/uPLSANRQ+8WiN/gXuhTY6wogQ==", - "dev": true, - "requires": { - "axios": "^0.17.0", - "brotli-size": "0.0.1", - "bytes": "^3.0.0", - "ci-env": "^1.4.0", - "commander": "^2.11.0", - "github-build": "^1.2.0", - "glob": "^7.1.2", - "gzip-size": "^4.0.0", - "prettycli": "^1.4.3", - "read-pkg-up": "^3.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "gzip-size": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-4.1.0.tgz", - "integrity": "sha1-iuCWJX6r59acRb4rZ8RIEk/7UXw=", - "dev": true, - "requires": { - "duplexer": "^0.1.1", - "pify": "^3.0.0" - } - } - } - }, "bunyan": { "version": "1.8.12", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", @@ -6777,12 +6665,6 @@ "moment": "^2.10.3" } }, - "ci-env": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ci-env/-/ci-env-1.7.0.tgz", - "integrity": "sha512-ifHfV5JmACoTnoPxwjKjUUAekL1UCKZ9EU27GaaSkLVopkV3H1w0eYIpY+aAiX31SVEtTrZFMS94EFETSj0vIA==", - "dev": true - }, "ci-info": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", @@ -7068,6 +6950,21 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "codemirror": { + "version": "5.43.0", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.43.0.tgz", + "integrity": "sha512-mljwQWUaWIf85I7QwTBryF2ASaIvmYAL4s5UCanCJFfKeXOKhrqdHWdHiZWAMNT+hjLTCnVx2S/SYTORIgxsgA==", + "dev": true + }, + "codemirror-spell-checker": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz", + "integrity": "sha1-HGYPkIlIPMtRE7m6nKGcP0mTNx4=", + "dev": true, + "requires": { + "typo-js": "*" + } + }, "collapse-white-space": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", @@ -7340,12 +7237,6 @@ "date-now": "^0.1.4" } }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, "consolidate": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.14.0.tgz", @@ -8660,15 +8551,6 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -8944,12 +8826,6 @@ "repeating": "^2.0.0" } }, - "detect-libc": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-0.2.0.tgz", - "integrity": "sha1-R/31ZzSKF+wl/L8LnkRjSKdvn7U=", - "dev": true - }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -10323,9 +10199,9 @@ "dev": true }, "enzyme": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.7.0.tgz", - "integrity": "sha512-QLWx+krGK6iDNyR1KlH5YPZqxZCQaVF6ike1eDJAOg0HvSkSCVImPsdWaNw6v+VrnK92Kg8jIOYhuOSS9sBpyg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.8.0.tgz", + "integrity": "sha512-bfsWo5nHyZm1O1vnIsbwdfhU989jk+squU9NKvB+Puwo5j6/Wg9pN5CO0YJelm98Dao3NPjkDZk+vvgwpMwYxw==", "dev": true, "requires": { "array.prototype.flat": "^1.2.1", @@ -10358,43 +10234,53 @@ } }, "enzyme-adapter-react-16": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.6.0.tgz", - "integrity": "sha512-ay9eGFpChyUDnjTFMMJHzrb681LF3hPWJLEA7RoLFG9jSWAdAm2V50pGmFV9dYGJgh5HfdiqM+MNvle41Yf/PA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.7.1.tgz", + "integrity": "sha512-OQXKgfHWyHN3sFu2nKj3mhgRcqIPIJX6aOzq5AHVFES4R9Dw/vCBZFMPyaG81g2AZ5DogVh39P3MMNUbqNLTcw==", "dev": true, "requires": { - "enzyme-adapter-utils": "^1.8.0", + "enzyme-adapter-utils": "^1.9.0", "function.prototype.name": "^1.1.0", "object.assign": "^4.1.0", "object.values": "^1.0.4", "prop-types": "^15.6.2", - "react-is": "^16.5.2", + "react-is": "^16.6.1", "react-test-renderer": "^16.0.0-0" }, "dependencies": { "react-is": { - "version": "16.5.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.5.2.tgz", - "integrity": "sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.7.0.tgz", + "integrity": "sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g==", "dev": true } } }, "enzyme-adapter-utils": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.8.1.tgz", - "integrity": "sha512-s3QB3xQAowaDS2sHhmEqrT13GJC4+n5bG015ZkLv60n9k5vhxxHTQRIneZmQ4hmdCZEBrvUJ89PG6fRI5OEeuQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz", + "integrity": "sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA==", "dev": true, "requires": { "function.prototype.name": "^1.1.0", "object.assign": "^4.1.0", - "prop-types": "^15.6.2" + "object.fromentries": "^2.0.0", + "prop-types": "^15.6.2", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + } } }, "enzyme-to-json": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.3.4.tgz", - "integrity": "sha1-Z8YEDpMRgvGDQYry659DIyWKp38=", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.3.5.tgz", + "integrity": "sha512-DmH1wJ68HyPqKSYXdQqB33ZotwfUhwQZW3IGXaNXgR69Iodaoj8TF/D9RjLdz4pEhGq2Tx2zwNUIjBuqoZeTgA==", "dev": true, "requires": { "lodash": "^4.17.4" @@ -10790,12 +10676,6 @@ } } }, - "expand-template": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz", - "integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg==", - "dev": true - }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -11378,9 +11258,9 @@ "integrity": "sha1-A5/fI/iCPkQMOPMnfm/vEXQhWzA=" }, "fluent": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/fluent/-/fluent-0.8.0.tgz", - "integrity": "sha512-bZfthhubEH1lKgGIi0fIDeNkZrfEOu3MrLbi284LdxNG+9Q5gq2KsuoocumqNPStVtWo3S3/1p8RIqd34u3Mzw==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/fluent/-/fluent-0.10.0.tgz", + "integrity": "sha512-2I6xaHecA76FiGavw6eKurXaHd6p25eenAgRYKSaMwJEpbvWpAlwFWAnC+BGzrGIKINUHwDlCQRtRenJvlbqQQ==", "dev": true }, "fluent-intl-polyfill": { @@ -11399,9 +11279,9 @@ "dev": true }, "fluent-react": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/fluent-react/-/fluent-react-0.8.0.tgz", - "integrity": "sha512-mO8iPb+muWopEDcbNgb66kr6Tl9tPjRpguDaez9W0xU1hiJ9fQ6EfdCxCUQOYsnVnbruLJqxaRsExSRrCamLCg==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fluent-react/-/fluent-react-0.8.3.tgz", + "integrity": "sha512-uNP+kefHxZ/yAqtg/cQlsruY/K8oiVMbU2E7Tcd+LEaVd5GxKWjv6CvmQNVrAsrWai0VrrVAg5SRVD19vVPHog==", "dev": true, "requires": { "cached-iterable": "^0.2.1", @@ -11620,12 +11500,6 @@ "readable-stream": "^2.0.0" } }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, "fs-extra": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", @@ -12211,18 +12085,6 @@ } } }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -12245,44 +12107,6 @@ "integrity": "sha1-YyDLlM5W7JdVyJred1vNuwNY1CU=", "dev": true }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, "get-caller-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", @@ -12320,41 +12144,6 @@ "assert-plus": "^1.0.0" } }, - "github-build": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/github-build/-/github-build-1.2.0.tgz", - "integrity": "sha512-Iq7NialLYz5yRZDkiX8zaOWd+N3BssJJfUvG7wd8r4MeLCN88SdxEYo2esseMLpLtP4vNXhgamg1eRm7hw59qw==", - "dev": true, - "requires": { - "axios": "0.15.3" - }, - "dependencies": { - "axios": { - "version": "0.15.3", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.15.3.tgz", - "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=", - "dev": true, - "requires": { - "follow-redirects": "1.0.0" - } - }, - "follow-redirects": { - "version": "1.0.0", - "resolved": "http://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", - "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=", - "dev": true, - "requires": { - "debug": "^2.2.0" - } - } - } - }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", - "dev": true - }, "github-slugger": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.0.tgz", @@ -13234,12 +13023,6 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -13894,18 +13677,6 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, - "iltorb": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/iltorb/-/iltorb-1.3.10.tgz", - "integrity": "sha512-nyB4+ru1u8CQqQ6w7YjasboKN3NQTN8GH/V/eEssNRKhW6UbdxdWhB9fJ5EEdjJfezKY0qPrcwLyIcgjL8hHxA==", - "dev": true, - "requires": { - "detect-libc": "^0.2.0", - "nan": "^2.6.2", - "node-gyp": "^3.6.2", - "prebuild-install": "^2.3.0" - } - }, "immutable": { "version": "3.7.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", @@ -17646,6 +17417,12 @@ "integrity": "sha1-iQwsGzv+g/sA5BKbjkz+ZFJw+dE=", "dev": true }, + "marked": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.0.tgz", + "integrity": "sha512-HduzIW2xApSXKXJSpCipSxKyvMbwRRa/TwMbepmlZziKdH8548WSoDP4SxzulEKjlo8BE39l+2fwJZuRKOln6g==", + "dev": true + }, "matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", @@ -17968,12 +17745,6 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", @@ -18227,7 +17998,8 @@ "nan": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "optional": true }, "nanoassert": { "version": "1.1.0", @@ -18296,16 +18068,24 @@ "optional": true }, "nearley": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.15.1.tgz", - "integrity": "sha512-8IUY/rUrKz2mIynUGh8k+tul1awMKEjeHHC5G3FHvvyAW6oq4mQfNp2c0BMea+sYZJvYcrrM6GmZVIle/GRXGw==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.16.0.tgz", + "integrity": "sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg==", "dev": true, "requires": { + "commander": "^2.19.0", "moo": "^0.4.3", - "nomnom": "~1.6.2", "railroad-diagrams": "^1.0.0", "randexp": "0.4.6", "semver": "^5.4.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + } } }, "negotiator": { @@ -18376,15 +18156,6 @@ "lower-case": "^1.1.1" } }, - "node-abi": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.5.0.tgz", - "integrity": "sha512-9g2twBGSP6wIR5PW7tXvAWnEWKJDH/VskdXp168xsw9VVxpEGov8K4jsP4/VeoC7b2ZAyzckvMCuQuQlw44lXg==", - "dev": true, - "requires": { - "semver": "^5.4.1" - } - }, "node-dir": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", @@ -18405,57 +18176,6 @@ "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", "dev": true }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "dev": true, - "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "semver": { - "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true - } - } - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -18527,30 +18247,6 @@ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.6.7.tgz", "integrity": "sha512-GIAAYvs9XIP1fBa8wR89ukUh3yjL44pom5LKY5nTZcL+Zp9sRkqL8wgskyBQECQg9CPsDX/fjTZx8MNz20t0jA==" }, - "nomnom": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", - "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", - "dev": true, - "requires": { - "colors": "0.5.x", - "underscore": "~1.4.4" - }, - "dependencies": { - "colors": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", - "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=", - "dev": true - } - } - }, - "noop-logger": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", - "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", - "dev": true - }, "nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", @@ -18671,18 +18367,6 @@ "which": "^1.2.10" } }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, "nth-check": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", @@ -18905,14 +18589,37 @@ } }, "object.entries": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz", - "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + }, + "dependencies": { + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + } + } + }, + "object.fromentries": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", + "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", "dev": true, "requires": { "define-properties": "^1.1.2", - "es-abstract": "^1.6.1", - "function-bind": "^1.1.0", + "es-abstract": "^1.11.0", + "function-bind": "^1.1.1", "has": "^1.0.1" } }, @@ -19143,16 +18850,6 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -22425,37 +22122,6 @@ "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.1.tgz", "integrity": "sha1-uRepB5smF42aJK9aXNjLSpkdEbk=" }, - "prebuild-install": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.3.tgz", - "integrity": "sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^1.0.2", - "github-from-package": "0.0.0", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "node-abi": "^2.2.0", - "noop-logger": "^0.1.1", - "npmlog": "^4.0.1", - "os-homedir": "^1.0.1", - "pump": "^2.0.1", - "rc": "^1.1.6", - "simple-get": "^2.7.0", - "tar-fs": "^1.13.0", - "tunnel-agent": "^0.6.0", - "which-pm-runs": "^1.0.0" - }, - "dependencies": { - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true - } - } - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -22529,52 +22195,6 @@ "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", "dev": true }, - "prettycli": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/prettycli/-/prettycli-1.4.3.tgz", - "integrity": "sha512-KLiwAXXfSWXZqGmZlnKPuGMTFp+0QbcySplL1ft9gfteT/BNsG64Xo8u2Qr9r+qnsIZWBQ66Zs8tg+8s2fmzvw==", - "dev": true, - "requires": { - "chalk": "2.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - } - } - }, "prismjs": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.15.0.tgz", @@ -22796,9 +22416,9 @@ "dev": true }, "raf": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.0.tgz", - "integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", "dev": true, "requires": { "performance-now": "^2.1.0" @@ -22922,14 +22542,14 @@ } }, "react": { - "version": "16.5.2", - "resolved": "https://registry.npmjs.org/react/-/react-16.5.2.tgz", - "integrity": "sha512-FDCSVd3DjVTmbEAjUNX6FgfAmQ+ypJfHUsqUJOYNCBUp1h8lqmtC+0mXJ+JjsWx4KAVTkk1vKd1hLQPvEviSuw==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.7.0.tgz", + "integrity": "sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "schedule": "^0.5.0" + "scheduler": "^0.12.0" } }, "react-adopt": { @@ -23234,14 +22854,14 @@ } }, "react-dom": { - "version": "16.5.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.5.2.tgz", - "integrity": "sha512-RC8LDw8feuZOHVgzEf7f+cxBr/DnKdqp56VU0lAs1f4UfKc4cU8wU4fTq/mgnvynLQo8OtlPC19NUFh/zjZPuA==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.7.0.tgz", + "integrity": "sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "schedule": "^0.5.0" + "scheduler": "^0.12.0" } }, "react-emotion": { @@ -23460,21 +23080,21 @@ "integrity": "sha512-rxlZtZk5t6Y3gqqpaZ1lxY3RqlQcBU5uGsSoZj/hbF3ZweDqPbFHDkczT4emAxeaw37OD96RAAoayFGFQZCdWg==" }, "react-test-renderer": { - "version": "16.5.2", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.5.2.tgz", - "integrity": "sha512-AGbJYbCVx1J6jdUgI4s0hNp+9LxlgzKvXl0ROA3DHTrtjAr00Po1RhDZ/eAq2VC/ww8AHgpDXULh5V2rhEqqJg==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.7.0.tgz", + "integrity": "sha512-tFbhSjknSQ6+ttzmuGdv+SjQfmvGcq3PFKyPItohwhhOBmRoTf1We3Mlt3rJtIn85mjPXOkKV+TaKK4irvk9Yg==", "dev": true, "requires": { "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "react-is": "^16.5.2", - "schedule": "^0.5.0" + "react-is": "^16.7.0", + "scheduler": "^0.12.0" }, "dependencies": { "react-is": { - "version": "16.5.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.5.2.tgz", - "integrity": "sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.7.0.tgz", + "integrity": "sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g==", "dev": true } } @@ -24695,11 +24315,12 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, - "schedule": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/schedule/-/schedule-0.5.0.tgz", - "integrity": "sha512-HUcJicG5Ou8xfR//c2rPT0lPIRR09vVvN81T9fqfVgBmhERUbDEQoYKjpBxbueJnCPpSu2ujXzOnRQt6x9o/jw==", + "scheduler": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.12.0.tgz", + "integrity": "sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==", "requires": { + "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, @@ -24944,23 +24565,6 @@ "integrity": "sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo=", "dev": true }, - "simple-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", - "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", - "dev": true - }, - "simple-get": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", - "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", - "dev": true, - "requires": { - "decompress-response": "^3.3.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -24978,6 +24582,17 @@ } } }, + "simplemde": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/simplemde/-/simplemde-1.11.2.tgz", + "integrity": "sha1-ojo12XjSxA7wfewAjJLwcNjggOM=", + "dev": true, + "requires": { + "codemirror": "*", + "codemirror-spell-checker": "*", + "marked": "*" + } + }, "simulant": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simulant/-/simulant-0.2.2.tgz", @@ -25940,56 +25555,6 @@ "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", "dev": true }, - "tar": { - "version": "2.2.1", - "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "dev": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.2", - "inherits": "2" - } - }, - "tar-fs": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", - "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", - "dev": true, - "requires": { - "chownr": "^1.0.1", - "mkdirp": "^0.5.1", - "pump": "^1.0.0", - "tar-stream": "^1.1.2" - }, - "dependencies": { - "pump": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", - "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "tar-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", - "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", - "dev": true, - "requires": { - "bl": "^1.0.0", - "buffer-alloc": "^1.2.0", - "end-of-stream": "^1.0.0", - "fs-constants": "^1.0.0", - "readable-stream": "^2.3.0", - "to-buffer": "^1.1.1", - "xtend": "^4.0.0" - } - }, "term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -26492,12 +26057,6 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, - "to-buffer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", - "dev": true - }, "to-capital-case": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-capital-case/-/to-capital-case-1.0.0.tgz", @@ -27226,9 +26785,9 @@ "dev": true }, "typescript": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.3.tgz", - "integrity": "sha512-kk80vLW9iGtjMnIv11qyxLqZm20UklzuR2tL0QAnDIygIUIemcZMxlMWudl9OOt76H3ntVzcTiddQ1/pAAJMYg==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", + "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==", "dev": true }, "typescript-snapshots-plugin": { @@ -27237,6 +26796,12 @@ "integrity": "sha1-4rp5y0C3Vc4tUp6h5fYlMyd5T5g=", "dev": true }, + "typo-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.0.3.tgz", + "integrity": "sha1-VNjrx5SfGngQkItgAsaEFSbJnVo=", + "dev": true + }, "ua-parser-js": { "version": "0.7.18", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz", @@ -27329,12 +26894,6 @@ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true }, - "underscore": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", - "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=", - "dev": true - }, "undertaker": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz", @@ -29033,21 +28592,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "which-pm-runs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "widest-line": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", diff --git a/package.json b/package.json index 845dd878a..0b8928c26 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "url": "git://github.com/coralproject/talk.git" }, "engines": { - "node": ">=8.9.0", - "npm": ">=6.4.1" + "node": ">=10.0.0", + "npm": ">=6.7.0" }, "bugs": "https://github.com/coralproject/talk/issues", "contributors": "https://github.com/coralproject/talk/graphs/contributors", @@ -21,7 +21,6 @@ "build": "npm-run-all generate --parallel build:*", "build:client": "ts-node ./scripts/build.ts", "build:server": "gulp server", - "bundlesize": "bundlesize", "generate": "npm-run-all --parallel generate:*", "generate:css-types": "tcm src/core/client/", "generate:relay-stream": "ts-node ./scripts/compileRelay --src ./src/core/client/stream --schema tenant", @@ -135,8 +134,8 @@ "@types/cors": "^2.8.4", "@types/cross-spawn": "^6.0.0", "@types/dotenv": "^4.0.3", - "@types/enzyme": "^3.1.11", - "@types/enzyme-adapter-react-16": "^1.0.2", + "@types/enzyme": "^3.1.15", + "@types/enzyme-adapter-react-16": "^1.0.3", "@types/escape-string-regexp": "^1.0.0", "@types/eventemitter2": "^4.1.0", "@types/express": "^4.16.0", @@ -165,15 +164,18 @@ "@types/passport-local": "^1.0.33", "@types/passport-oauth2": "^1.4.5", "@types/passport-strategy": "^0.2.33", + "@types/prop-types": "^15.5.8", + "@types/react": "^16.7.20", "@types/react-copy-to-clipboard": "^4.2.5", - "@types/react-dom": "^16.0.6", + "@types/react-dom": "^16.0.11", "@types/react-relay": "^1.3.9", "@types/react-responsive": "^3.0.1", - "@types/react-test-renderer": "^16.0.1", + "@types/react-test-renderer": "^16.0.3", "@types/react-transition-group": "^2.0.14", "@types/recompose": "^0.26.5", "@types/relay-runtime": "^1.3.6", "@types/sane": "^2.0.0", + "@types/simplemde": "^1.11.7", "@types/sinon": "^5.0.1", "@types/source-map-support": "^0.4.1", "@types/stack-utils": "^1.0.1", @@ -198,7 +200,6 @@ "babel-plugin-use-lodash-es": "^0.2.0", "babel-preset-react-optimize": "^1.0.1", "bowser": "^1.9.4", - "bundlesize": "^0.17.0", "case-sensitive-paths-webpack-plugin": "^2.1.2", "chalk": "^2.4.1", "chokidar": "^2.0.4", @@ -211,16 +212,16 @@ "css-loader": "^1.0.1", "del": "^3.0.0", "docz": "^0.5.8", - "enzyme": "^3.7.0", - "enzyme-adapter-react-16": "^1.6.0", - "enzyme-to-json": "^3.3.4", + "enzyme": "^3.8.0", + "enzyme-adapter-react-16": "^1.7.1", + "enzyme-to-json": "^3.3.5", "eventemitter2": "^5.0.1", "final-form": "^4.8.1", "flat": "^4.1.0", - "fluent": "^0.8.0", + "fluent": "^0.10.0", "fluent-intl-polyfill": "^0.1.0", "fluent-langneg": "^0.1.0", - "fluent-react": "^0.8.0", + "fluent-react": "^0.8.3", "graphql-schema-typescript": "^1.2.1", "gulp": "^4.0.0", "gulp-babel": "^8.0.0", @@ -256,15 +257,15 @@ "pym.js": "^1.3.2", "querystringify": "^2.1.0", "raw-loader": "^0.5.1", - "react": "^16.5.2", + "react": "^16.7.0", "react-copy-to-clipboard": "^5.0.1", "react-dev-utils": "6.0.0-next.3e165448", - "react-dom": "^16.5.2", + "react-dom": "^16.7.0", "react-final-form": "^3.6.4", "react-popper": "^1.3.2", "react-relay": "^1.7.0-rc.1", "react-responsive": "^5.0.0", - "react-test-renderer": "^16.5.2", + "react-test-renderer": "^16.7.0", "react-timeago": "^4.1.9", "react-transition-group": "^2.5.0", "react-with-state-props": "^2.0.4", @@ -274,6 +275,7 @@ "relay-local-schema": "^0.7.0", "relay-runtime": "^1.7.0-rc.1", "sane": "^4.0.2", + "simplemde": "^1.11.2", "simulant": "^0.2.2", "sinon": "^6.1.5", "style-loader": "^0.23.1", @@ -292,7 +294,7 @@ "typed-css-modules": "^0.3.4", "typeface-manuale": "0.0.54", "typeface-source-sans-pro": "0.0.54", - "typescript": "^3.0.3", + "typescript": "3.1.6", "typescript-snapshots-plugin": "^1.2.0", "wait-for-expect": "^1.1.0", "webpack": "^4.27.1", diff --git a/src/core/build/publicPath.js b/src/core/build/publicPath.js index e3e6fd334..3de2c5854 100644 --- a/src/core/build/publicPath.js +++ b/src/core/build/publicPath.js @@ -1,3 +1,2 @@ -__webpack_public_path__ = JSON.parse( - document.getElementById("config").innerText -); +__webpack_public_path__ = + JSON.parse(document.getElementById("config").innerText) || "/"; diff --git a/src/core/client/admin/components/DecisionHistoryButton.tsx b/src/core/client/admin/components/DecisionHistoryButton.tsx index bd51cc8db..a6f5021b1 100644 --- a/src/core/client/admin/components/DecisionHistoryButton.tsx +++ b/src/core/client/admin/components/DecisionHistoryButton.tsx @@ -41,7 +41,7 @@ class DecisionHistoryButton extends React.Component { this.toggleVisibilityOncePerFrame(toggleVisibility)} aria-controls={popoverID} - forwardRef={forwardRef} + ref={forwardRef} className={styles.historyIcon} data-testid="decisionHistory-toggle" > diff --git a/src/core/client/admin/components/__snapshots__/AuthBox.spec.tsx.snap b/src/core/client/admin/components/__snapshots__/AuthBox.spec.tsx.snap index 5c94d7c95..9eab086d7 100644 --- a/src/core/client/admin/components/__snapshots__/AuthBox.spec.tsx.snap +++ b/src/core/client/admin/components/__snapshots__/AuthBox.spec.tsx.snap @@ -4,14 +4,14 @@ exports[`renders correctly 1`] = `
- - -
-
+
- title - +
content -
-
+ +
`; diff --git a/src/core/client/admin/components/__snapshots__/SignOutButton.spec.tsx.snap b/src/core/client/admin/components/__snapshots__/SignOutButton.spec.tsx.snap index 23ffa5fa0..31e45708a 100644 --- a/src/core/client/admin/components/__snapshots__/SignOutButton.spec.tsx.snap +++ b/src/core/client/admin/components/__snapshots__/SignOutButton.spec.tsx.snap @@ -4,12 +4,12 @@ exports[`renders correctly 1`] = ` - Sign Out - + `; diff --git a/src/core/client/admin/mutations/UpdateSettingsMutation.ts b/src/core/client/admin/mutations/UpdateSettingsMutation.ts index 162adb704..50185fc6f 100644 --- a/src/core/client/admin/mutations/UpdateSettingsMutation.ts +++ b/src/core/client/admin/mutations/UpdateSettingsMutation.ts @@ -19,16 +19,13 @@ const mutation = graphql` updateSettings(input: $input) { settings { auth { - ...FacebookConfigContainer_auth - ...FacebookConfigContainer_authReadOnly - ...GoogleConfigContainer_auth - ...GoogleConfigContainer_authReadOnly - ...SSOConfigContainer_auth - ...SSOConfigContainer_authReadOnly - ...OIDCConfigContainer_auth - ...OIDCConfigContainer_authReadOnly - ...DisplayNamesConfigContainer_auth + ...AuthConfigContainer_auth } + ...ModerationConfigContainer_settings + ...GeneralConfigContainer_settings + ...OrganizationConfigContainer_settings + ...WordListConfigContainer_settings + ...AdvancedConfigContainer_settings } clientMutationId } diff --git a/src/core/client/admin/routeConfig.tsx b/src/core/client/admin/routeConfig.tsx index 3ae5a1d9e..367fef205 100644 --- a/src/core/client/admin/routeConfig.tsx +++ b/src/core/client/admin/routeConfig.tsx @@ -4,9 +4,13 @@ import React from "react"; import App from "./components/App"; import AuthCheckContainer from "./containers/AuthCheckContainer"; import Community from "./routes/community/components/Community"; -import ConfigureModeration from "./routes/configure/components/Moderation"; import ConfigureContainer from "./routes/configure/containers/ConfigureContainer"; -import ConfigureAuthRouteContainer from "./routes/configure/sections/auth/containers/AuthRouteContainer"; +import ConfigureAdvancedRouteContainer from "./routes/configure/sections/advanced/containers/AdvancedConfigRouteContainer"; +import ConfigureAuthRouteContainer from "./routes/configure/sections/auth/containers/AuthConfigRouteContainer"; +import ConfigureGeneralRouteContainer from "./routes/configure/sections/general/containers/GeneralConfigRouteContainer"; +import ConfigureModerationRouteContainer from "./routes/configure/sections/moderation/containers/ModerationConfigRouteContainer"; +import ConfigureOrganizationRouteContainer from "./routes/configure/sections/organization/containers/OrganizationRouteContainer"; +import ConfigureWordListRouteContainer from "./routes/configure/sections/wordList/containers/WordListRouteContainer"; import LoginContainer from "./routes/login/containers/LoginContainer"; import ModerateContainer from "./routes/moderate/containers/ModerateContainer"; import { @@ -40,9 +44,28 @@ export default makeRouteConfig( - - + + + + + + diff --git a/src/core/client/admin/routes/community/components/__snapshots__/Community.spec.tsx.snap b/src/core/client/admin/routes/community/components/__snapshots__/Community.spec.tsx.snap index ee6c74d9f..de6042ba1 100644 --- a/src/core/client/admin/routes/community/components/__snapshots__/Community.spec.tsx.snap +++ b/src/core/client/admin/routes/community/components/__snapshots__/Community.spec.tsx.snap @@ -2,10 +2,10 @@ exports[`renders correctly 1`] = ` - Community - + `; diff --git a/src/core/client/admin/routes/configure/components/ConfigurationSubHeader.tsx b/src/core/client/admin/routes/configure/components/ConfigurationSubHeader.tsx new file mode 100644 index 000000000..9ef516b29 --- /dev/null +++ b/src/core/client/admin/routes/configure/components/ConfigurationSubHeader.tsx @@ -0,0 +1,12 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; + +import Subheader from "../components/Subheader"; + +const ConfigurationSubHeader: StatelessComponent<{}> = () => ( + }> + Configuration + +); + +export default ConfigurationSubHeader; diff --git a/src/core/client/admin/routes/configure/components/Configure.tsx b/src/core/client/admin/routes/configure/components/Configure.tsx index 0f0a68a7c..369db3340 100644 --- a/src/core/client/admin/routes/configure/components/Configure.tsx +++ b/src/core/client/admin/routes/configure/components/Configure.tsx @@ -30,9 +30,25 @@ const Configure: StatelessComponent = ({ - Moderation + + General + + + Organization + + + Moderation + + + + Banned and Suspect Words + + - Auth + Authentication + + + Advanced diff --git a/src/core/client/admin/routes/configure/components/Header.css b/src/core/client/admin/routes/configure/components/Header.css index 18daf9bde..f520676fc 100644 --- a/src/core/client/admin/routes/configure/components/Header.css +++ b/src/core/client/admin/routes/configure/components/Header.css @@ -2,4 +2,6 @@ padding-bottom: calc(0.5 * var(--spacing-unit)); border-bottom: 1px solid var(--palette-text-primary); margin-bottom: calc(1.5 * var(--spacing-unit)); + display: block; + width: 100%; } diff --git a/src/core/client/admin/routes/configure/components/Header.tsx b/src/core/client/admin/routes/configure/components/Header.tsx index 01642289d..2f21ea17b 100644 --- a/src/core/client/admin/routes/configure/components/Header.tsx +++ b/src/core/client/admin/routes/configure/components/Header.tsx @@ -1,10 +1,23 @@ +import cn from "classnames"; import React, { StatelessComponent } from "react"; + import { Typography } from "talk-ui/components"; +import { PropTypesOf } from "talk-ui/types"; import styles from "./Header.css"; -const Header: StatelessComponent = ({ children }) => ( - +type Props = PropTypesOf; + +const Header: StatelessComponent = ({ + children, + className, + ...rest +}) => ( + {children} ); diff --git a/src/core/client/admin/routes/configure/components/Moderation.tsx b/src/core/client/admin/routes/configure/components/Moderation.tsx deleted file mode 100644 index 13414b244..000000000 --- a/src/core/client/admin/routes/configure/components/Moderation.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { StatelessComponent } from "react"; - -import { Typography } from "talk-ui/components"; - -import Header from "./Header"; - -const Moderation: StatelessComponent = ({ children }) => ( -
-
Perspective Toxic Comment Filter
- - Using the Perspective API, the Toxic Comment filter warns users when - comments exceed the predefined toxicity threshold. Toxic comments will not - be published and are placed in the Pending Queue for review by a - moderator. If approved by a moderator, the comment will be published. - -
-); - -export default Moderation; diff --git a/src/core/client/admin/routes/configure/components/Navigation/Link.css b/src/core/client/admin/routes/configure/components/Navigation/Link.css index 1f821a4a7..f3c70f0fc 100644 --- a/src/core/client/admin/routes/configure/components/Navigation/Link.css +++ b/src/core/client/admin/routes/configure/components/Navigation/Link.css @@ -1,26 +1,18 @@ .link { + composes: sideNavigationItem from "talk-ui/shared/typography.css"; display: inline-block; - text-decoration: none; - text-transform: uppercase; padding: calc(0.5 * var(--spacing-unit)) var(--spacing-unit) calc(0.5 * var(--spacing-unit)) calc(var(--spacing-unit) + 2px); margin-left: 2px; border-left: 1px solid var(--palette-grey-lighter); - color: var(--palette-text-primary); - font-family: var(--font-family-sans-serif); - font-weight: var(--font-weight-regular); - font-size: calc(18rem / var(--rem-base)); - line-height: calc(20em / 18); - letter-spacing: calc(0.2em / 18); - &:hover { cursor: pointer; } } .linkActive { - font-weight: var(--font-weight-bold); + composes: sideNavigationActive from "talk-ui/shared/typography.css"; margin-left: 0px; border-left: calc(0.5 * var(--spacing-unit)) solid var(--palette-brand-main); padding-left: var(--spacing-unit); diff --git a/src/core/client/admin/routes/configure/components/OnOffField.tsx b/src/core/client/admin/routes/configure/components/OnOffField.tsx new file mode 100644 index 000000000..75ad343ca --- /dev/null +++ b/src/core/client/admin/routes/configure/components/OnOffField.tsx @@ -0,0 +1,61 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { parseStringBool } from "talk-framework/lib/form"; +import { Validator } from "talk-framework/lib/validation"; +import { RadioButton } from "talk-ui/components"; + +interface Props { + validate?: Validator; + name: string; + disabled: boolean; + invert?: boolean; +} + +const OnOffField: StatelessComponent = ({ + name, + disabled, + invert = false, +}) => ( +
+ + {({ input }) => ( + + + On + + + )} + + + {({ input }) => ( + + + Off + + + )} + +
+); + +export default OnOffField; diff --git a/src/core/client/admin/routes/configure/components/PermissionField.tsx b/src/core/client/admin/routes/configure/components/PermissionField.tsx new file mode 100644 index 000000000..dfde306ea --- /dev/null +++ b/src/core/client/admin/routes/configure/components/PermissionField.tsx @@ -0,0 +1,61 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { parseStringBool } from "talk-framework/lib/form"; +import { Validator } from "talk-framework/lib/validation"; +import { RadioButton } from "talk-ui/components"; + +interface Props { + validate?: Validator; + name: string; + disabled: boolean; + invert?: boolean; +} + +const PermissionField: StatelessComponent = ({ + name, + disabled, + invert = false, +}) => ( +
+ + {({ input }) => ( + + + Allow + + + )} + + + {({ input }) => ( + + + Don't allow + + + )} + +
+); + +export default PermissionField; diff --git a/src/core/client/admin/routes/configure/components/Subheader.css b/src/core/client/admin/routes/configure/components/Subheader.css new file mode 100644 index 000000000..cb8b2e057 --- /dev/null +++ b/src/core/client/admin/routes/configure/components/Subheader.css @@ -0,0 +1,5 @@ +.root { + padding-bottom: calc(0.25 * var(--spacing-unit)); + border-bottom: 1px solid var(--palette-divider); + margin-bottom: calc(0.75 * var(--spacing-unit)); +} diff --git a/src/core/client/admin/routes/configure/components/Moderation.spec.tsx b/src/core/client/admin/routes/configure/components/Subheader.spec.tsx similarity index 69% rename from src/core/client/admin/routes/configure/components/Moderation.spec.tsx rename to src/core/client/admin/routes/configure/components/Subheader.spec.tsx index 40e7ac219..c28a8a7b9 100644 --- a/src/core/client/admin/routes/configure/components/Moderation.spec.tsx +++ b/src/core/client/admin/routes/configure/components/Subheader.spec.tsx @@ -3,13 +3,13 @@ import { createRenderer } from "react-test-renderer/shallow"; import { PropTypesOf } from "talk-framework/types"; -import Moderation from "./Moderation"; +import Subheader from "./Subheader"; it("renders correctly", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { children: "child", }; const renderer = createRenderer(); - renderer.render(); + renderer.render(); expect(renderer.getRenderOutput()).toMatchSnapshot(); }); diff --git a/src/core/client/admin/routes/configure/components/Subheader.tsx b/src/core/client/admin/routes/configure/components/Subheader.tsx new file mode 100644 index 000000000..9280da585 --- /dev/null +++ b/src/core/client/admin/routes/configure/components/Subheader.tsx @@ -0,0 +1,12 @@ +import React, { StatelessComponent } from "react"; +import { Typography } from "talk-ui/components"; + +import styles from "./Subheader.css"; + +const Subheader: StatelessComponent = ({ children }) => ( + + {children} + +); + +export default Subheader; diff --git a/src/core/client/admin/routes/configure/sections/auth/components/ValidationMessage.css b/src/core/client/admin/routes/configure/components/ValidationMessage.css similarity index 100% rename from src/core/client/admin/routes/configure/sections/auth/components/ValidationMessage.css rename to src/core/client/admin/routes/configure/components/ValidationMessage.css diff --git a/src/core/client/admin/routes/configure/components/ValidationMessage.tsx b/src/core/client/admin/routes/configure/components/ValidationMessage.tsx new file mode 100644 index 000000000..8e144850d --- /dev/null +++ b/src/core/client/admin/routes/configure/components/ValidationMessage.tsx @@ -0,0 +1,21 @@ +import React, { StatelessComponent } from "react"; + +import { ValidationMessage as UIValidationMessage } from "talk-ui/components"; + +import { PropTypesOf } from "talk-ui/types"; +import styles from "./ValidationMessage.css"; + +interface Props extends PropTypesOf { + children: React.ReactNode; +} + +const ValidationMessage: StatelessComponent = ({ + children, + ...rest +}) => ( + + {children} + +); + +export default ValidationMessage; diff --git a/src/core/client/admin/routes/configure/components/__snapshots__/ConfigBox.spec.tsx.snap b/src/core/client/admin/routes/configure/components/__snapshots__/ConfigBox.spec.tsx.snap index bfab5d597..e7c90d45d 100644 --- a/src/core/client/admin/routes/configure/components/__snapshots__/ConfigBox.spec.tsx.snap +++ b/src/core/client/admin/routes/configure/components/__snapshots__/ConfigBox.spec.tsx.snap @@ -4,7 +4,7 @@ exports[`renders correctly 1`] = `
- @@ -18,7 +18,7 @@ exports[`renders correctly 1`] = ` topRight
- +
diff --git a/src/core/client/admin/routes/configure/components/__snapshots__/Header.spec.tsx.snap b/src/core/client/admin/routes/configure/components/__snapshots__/Header.spec.tsx.snap index c4b8ab3ff..21a12c578 100644 --- a/src/core/client/admin/routes/configure/components/__snapshots__/Header.spec.tsx.snap +++ b/src/core/client/admin/routes/configure/components/__snapshots__/Header.spec.tsx.snap @@ -1,10 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders correctly 1`] = ` - child - + `; diff --git a/src/core/client/admin/routes/configure/components/__snapshots__/Layout.spec.tsx.snap b/src/core/client/admin/routes/configure/components/__snapshots__/Layout.spec.tsx.snap index 75a06c012..c446c0b7c 100644 --- a/src/core/client/admin/routes/configure/components/__snapshots__/Layout.spec.tsx.snap +++ b/src/core/client/admin/routes/configure/components/__snapshots__/Layout.spec.tsx.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders correctly 1`] = ` - child - + `; diff --git a/src/core/client/admin/routes/configure/components/__snapshots__/Moderation.spec.tsx.snap b/src/core/client/admin/routes/configure/components/__snapshots__/Moderation.spec.tsx.snap deleted file mode 100644 index bb9252f68..000000000 --- a/src/core/client/admin/routes/configure/components/__snapshots__/Moderation.spec.tsx.snap +++ /dev/null @@ -1,12 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly 1`] = ` -
-
- Perspective Toxic Comment Filter -
- - Using the Perspective API, the Toxic Comment filter warns users when comments exceed the predefined toxicity threshold. Toxic comments will not be published and are placed in the Pending Queue for review by a moderator. If approved by a moderator, the comment will be published. - -
-`; diff --git a/src/core/client/admin/routes/configure/components/__snapshots__/Subheader.spec.tsx.snap b/src/core/client/admin/routes/configure/components/__snapshots__/Subheader.spec.tsx.snap new file mode 100644 index 000000000..ce973362a --- /dev/null +++ b/src/core/client/admin/routes/configure/components/__snapshots__/Subheader.spec.tsx.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + + child + +`; diff --git a/src/core/client/admin/routes/configure/containers/ConfigureContainer.tsx b/src/core/client/admin/routes/configure/containers/ConfigureContainer.tsx index 4d103ac4f..5f4d35341 100644 --- a/src/core/client/admin/routes/configure/containers/ConfigureContainer.tsx +++ b/src/core/client/admin/routes/configure/containers/ConfigureContainer.tsx @@ -102,7 +102,7 @@ class ConfigureContainer extends React.Component { private addSubmitHook: AddSubmitHook = hook => { this.submitHooks.push(hook); return () => { - this.submitHooks = this.submitHooks.filter(h => h === hook); + this.submitHooks = this.submitHooks.filter(h => h !== hook); }; }; diff --git a/src/core/client/admin/routes/configure/sections/advanced/components/AdvancedConfig.tsx b/src/core/client/admin/routes/configure/sections/advanced/components/AdvancedConfig.tsx new file mode 100644 index 000000000..b7b689f74 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/advanced/components/AdvancedConfig.tsx @@ -0,0 +1,35 @@ +import React, { StatelessComponent } from "react"; + +import { PropTypesOf } from "talk-framework/types"; +import { HorizontalGutter } from "talk-ui/components"; + +import CustomCSSConfigContainer from "../containers/CustomCSSConfigContainer"; +import PermittedDomainsConfigContainer from "../containers/PermittedDomainsConfigContainer"; + +interface Props { + disabled: boolean; + settings: PropTypesOf["settings"] & + PropTypesOf["settings"]; + onInitValues: (values: any) => void; +} + +const AdvancedConfig: StatelessComponent = ({ + disabled, + settings, + onInitValues, +}) => ( + + + + +); + +export default AdvancedConfig; diff --git a/src/core/client/admin/routes/configure/sections/advanced/components/CustomCSSConfig.tsx b/src/core/client/admin/routes/configure/sections/advanced/components/CustomCSSConfig.tsx new file mode 100644 index 000000000..ed7e83d30 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/advanced/components/CustomCSSConfig.tsx @@ -0,0 +1,64 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { + FormField, + HorizontalGutter, + TextField, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import Header from "../../../components/Header"; + +interface Props { + disabled: boolean; +} + +const CustomCSSConfig: StatelessComponent = ({ disabled }) => ( + + + +
}> + Custom CSS +
+
+ } + > + + URL of a CSS stylesheet that will override default Embed Stream + styles. Can be internal or external. + + + + {({ input, meta }) => ( + <> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + +
+
+); + +export default CustomCSSConfig; diff --git a/src/core/client/admin/routes/configure/sections/advanced/components/PermittedDomainsConfig.tsx b/src/core/client/admin/routes/configure/sections/advanced/components/PermittedDomainsConfig.tsx new file mode 100644 index 000000000..c4f19df86 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/advanced/components/PermittedDomainsConfig.tsx @@ -0,0 +1,65 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { formatStringList, parseStringList } from "talk-framework/lib/form"; +import { + FormField, + HorizontalGutter, + TextField, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import Header from "../../../components/Header"; + +interface Props { + disabled: boolean; +} + +const PermittedDomainsConfig: StatelessComponent = ({ disabled }) => ( + + + +
}> + Permitted Domains +
+
+ } + > + + Domains where your Talk instance is allowed to be embedded. Typical + use is localhost, staging.yourdomain.com, yourdomain.com, etc. + + + + {({ input, meta }) => ( + <> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + +
+
+); + +export default PermittedDomainsConfig; diff --git a/src/core/client/admin/routes/configure/sections/advanced/containers/AdvancedConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/advanced/containers/AdvancedConfigContainer.tsx new file mode 100644 index 000000000..ad5654b30 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/advanced/containers/AdvancedConfigContainer.tsx @@ -0,0 +1,54 @@ +import { FormApi } from "final-form"; +import { RouteProps } from "found"; +import { merge } from "lodash"; +import React from "react"; +import { graphql } from "react-relay"; + +import { AdvancedConfigContainer_settings as SettingsData } from "talk-admin/__generated__/AdvancedConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import AdvancedConfig from "../components/AdvancedConfig"; + +interface Props { + form: FormApi; + submitting: boolean; + settings: SettingsData; +} + +class AdvancedConfigContainer extends React.Component { + 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 = merge({}, this.initialValues, values); + }; + + public render() { + return ( + + ); + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment AdvancedConfigContainer_settings on Settings { + ...CustomCSSConfigContainer_settings + ...PermittedDomainsConfigContainer_settings + } + `, +})(AdvancedConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/advanced/containers/AdvancedConfigRouteContainer.tsx b/src/core/client/admin/routes/configure/sections/advanced/containers/AdvancedConfigRouteContainer.tsx new file mode 100644 index 000000000..4566df84c --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/advanced/containers/AdvancedConfigRouteContainer.tsx @@ -0,0 +1,47 @@ +import { FormApi } from "final-form"; +import React from "react"; +import { graphql } from "react-relay"; + +import { AdvancedConfigRouteContainerQueryResponse } from "talk-admin/__generated__/AdvancedConfigRouteContainerQuery.graphql"; +import { withRouteConfig } from "talk-framework/lib/router"; +import { Delay, Spinner } from "talk-ui/components"; + +import AdvancedConfigContainer from "./AdvancedConfigContainer"; + +interface Props { + data: AdvancedConfigRouteContainerQueryResponse | null; + form: FormApi; + submitting: boolean; +} + +class AdvancedConfigRouteContainer extends React.Component { + public render() { + if (!this.props.data) { + return ( + + + + ); + } + return ( + + ); + } +} + +const enhanced = withRouteConfig({ + query: graphql` + query AdvancedConfigRouteContainerQuery { + settings { + ...AdvancedConfigContainer_settings + } + } + `, + cacheConfig: { force: true }, +})(AdvancedConfigRouteContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/advanced/containers/CustomCSSConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/advanced/containers/CustomCSSConfigContainer.tsx new file mode 100644 index 000000000..bb1f6c3f8 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/advanced/containers/CustomCSSConfigContainer.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { CustomCSSConfigContainer_settings as SettingsData } from "talk-admin/__generated__/CustomCSSConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import CustomCSSConfig from "../components/CustomCSSConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class CustomCSSConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment CustomCSSConfigContainer_settings on Settings { + customCssUrl + } + `, +})(CustomCSSConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/advanced/containers/PermittedDomainsConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/advanced/containers/PermittedDomainsConfigContainer.tsx new file mode 100644 index 000000000..7fa83a873 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/advanced/containers/PermittedDomainsConfigContainer.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { PermittedDomainsConfigContainer_settings as SettingsData } from "talk-admin/__generated__/PermittedDomainsConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import PermittedDomainsConfig from "../components/PermittedDomainsConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class PermittedDomainsConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment PermittedDomainsConfigContainer_settings on Settings { + domains + } + `, +})(PermittedDomainsConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/auth/components/Auth.tsx b/src/core/client/admin/routes/configure/sections/auth/components/AuthConfig.tsx similarity index 87% rename from src/core/client/admin/routes/configure/sections/auth/components/Auth.tsx rename to src/core/client/admin/routes/configure/sections/auth/components/AuthConfig.tsx index a7dec0d59..ef9f9843f 100644 --- a/src/core/client/admin/routes/configure/sections/auth/components/Auth.tsx +++ b/src/core/client/admin/routes/configure/sections/auth/components/AuthConfig.tsx @@ -13,7 +13,11 @@ interface Props { onInitValues: (values: any) => void; } -const Auth: StatelessComponent = ({ disabled, auth, onInitValues }) => ( +const AuthConfig: StatelessComponent = ({ + disabled, + auth, + onInitValues, +}) => ( = ({ disabled, auth, onInitValues }) => ( ); -export default Auth; +export default AuthConfig; diff --git a/src/core/client/admin/routes/configure/sections/auth/components/ClientIDField.tsx b/src/core/client/admin/routes/configure/sections/auth/components/ClientIDField.tsx index c8512b627..506f2fe38 100644 --- a/src/core/client/admin/routes/configure/sections/auth/components/ClientIDField.tsx +++ b/src/core/client/admin/routes/configure/sections/auth/components/ClientIDField.tsx @@ -6,7 +6,7 @@ import { Field } from "react-final-form"; import { Validator } from "talk-framework/lib/validation"; import { FormField, InputLabel, TextField } from "talk-ui/components"; -import ValidationMessage from "./ValidationMessage"; +import ValidationMessage from "../../../components/ValidationMessage"; interface Props { validate?: Validator; diff --git a/src/core/client/admin/routes/configure/sections/auth/components/ClientSecretField.tsx b/src/core/client/admin/routes/configure/sections/auth/components/ClientSecretField.tsx index fbd35f82e..1d843fc7d 100644 --- a/src/core/client/admin/routes/configure/sections/auth/components/ClientSecretField.tsx +++ b/src/core/client/admin/routes/configure/sections/auth/components/ClientSecretField.tsx @@ -6,7 +6,7 @@ import { Field } from "react-final-form"; import { Validator } from "talk-framework/lib/validation"; import { FormField, InputLabel, TextField } from "talk-ui/components"; -import ValidationMessage from "./ValidationMessage"; +import ValidationMessage from "../../../components/ValidationMessage"; interface Props { validate?: Validator; diff --git a/src/core/client/admin/routes/configure/sections/auth/components/DisplayNamesConfig.tsx b/src/core/client/admin/routes/configure/sections/auth/components/DisplayNamesConfig.tsx index 4622f1c83..d37fd0ed1 100644 --- a/src/core/client/admin/routes/configure/sections/auth/components/DisplayNamesConfig.tsx +++ b/src/core/client/admin/routes/configure/sections/auth/components/DisplayNamesConfig.tsx @@ -2,6 +2,7 @@ import { Localized } from "fluent-react/compat"; import React, { StatelessComponent } from "react"; import { Field } from "react-final-form"; +import { parseStringBool } from "talk-framework/lib/form"; import { Flex, FormField, @@ -16,20 +17,18 @@ interface Props { disabled?: boolean; } -const parseStringBool = (v: string) => v === "true"; - const DisplayNamesConfig: StatelessComponent = ({ disabled }) => (
Display Names
- + Some AUTH integrations include a Display Name as well as a User Name. - + A User Name has to be unique (there can only be one Juan_Doe, for example), whereas a Display Name does not. If your AUTH provider allows for Display Names, you can enable this option. This allows for fewer diff --git a/src/core/client/admin/routes/configure/sections/auth/components/OIDCConfig.tsx b/src/core/client/admin/routes/configure/sections/auth/components/OIDCConfig.tsx index 17e52df47..6ee6200e9 100644 --- a/src/core/client/admin/routes/configure/sections/auth/components/OIDCConfig.tsx +++ b/src/core/client/admin/routes/configure/sections/auth/components/OIDCConfig.tsx @@ -28,7 +28,8 @@ import ConfigDescription from "./ConfigDescription"; import RedirectField from "./RedirectField"; import RegistrationField from "./RegistrationField"; import TargetFilterField from "./TargetFilterField"; -import ValidationMessage from "./ValidationMessage"; + +import ValidationMessage from "../../../components/ValidationMessage"; interface Props { disabled?: boolean; diff --git a/src/core/client/admin/routes/configure/sections/auth/components/ValidationMessage.tsx b/src/core/client/admin/routes/configure/sections/auth/components/ValidationMessage.tsx deleted file mode 100644 index 40fb6300b..000000000 --- a/src/core/client/admin/routes/configure/sections/auth/components/ValidationMessage.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, { StatelessComponent } from "react"; - -import { ValidationMessage as UIValidationMessage } from "talk-ui/components"; - -import styles from "./ValidationMessage.css"; - -interface Props { - children: React.ReactNode; -} - -const ValidationMessage: StatelessComponent = ({ children }) => ( - {children} -); - -export default ValidationMessage; diff --git a/src/core/client/admin/routes/configure/sections/auth/containers/AuthContainer.tsx b/src/core/client/admin/routes/configure/sections/auth/containers/AuthConfigContainer.tsx similarity index 91% rename from src/core/client/admin/routes/configure/sections/auth/containers/AuthContainer.tsx rename to src/core/client/admin/routes/configure/sections/auth/containers/AuthConfigContainer.tsx index 4e7e40166..f80429574 100644 --- a/src/core/client/admin/routes/configure/sections/auth/containers/AuthContainer.tsx +++ b/src/core/client/admin/routes/configure/sections/auth/containers/AuthConfigContainer.tsx @@ -5,7 +5,7 @@ import { get, merge } from "lodash"; import React from "react"; import { graphql } from "react-relay"; -import { AuthContainer_auth as AuthData } from "talk-admin/__generated__/AuthContainer_auth.graphql"; +import { AuthConfigContainer_auth as AuthData } from "talk-admin/__generated__/AuthConfigContainer_auth.graphql"; import { TalkContext, withContext } from "talk-framework/lib/bootstrap"; import { getMessage } from "talk-framework/lib/i18n"; @@ -16,7 +16,7 @@ import { SubmitHook, withSubmitHookContext, } from "../../../submitHook"; -import Auth from "../components/Auth"; +import AuthConfig from "../components/AuthConfig"; interface Props { localeBundles: TalkContext["localeBundles"]; @@ -26,7 +26,7 @@ interface Props { auth: AuthData; } -class AuthContainer extends React.Component { +class AuthConfigContainer extends React.Component { public static routeConfig: RouteProps; private initialValues = {}; private removeSubmitHook: RemoveSubmitHook; @@ -85,7 +85,7 @@ class AuthContainer extends React.Component { public render() { return ( - { const enhanced = withFragmentContainer({ auth: graphql` - fragment AuthContainer_auth on Auth { + fragment AuthConfigContainer_auth on Auth { ...FacebookConfigContainer_auth ...FacebookConfigContainer_authReadOnly ...GoogleConfigContainer_auth @@ -111,7 +111,7 @@ const enhanced = withFragmentContainer({ `, })( withSubmitHookContext(addSubmitHook => ({ addSubmitHook }))( - withContext(({ localeBundles }) => ({ localeBundles }))(AuthContainer) + withContext(({ localeBundles }) => ({ localeBundles }))(AuthConfigContainer) ) ); diff --git a/src/core/client/admin/routes/configure/sections/auth/containers/AuthRouteContainer.tsx b/src/core/client/admin/routes/configure/sections/auth/containers/AuthConfigRouteContainer.tsx similarity index 70% rename from src/core/client/admin/routes/configure/sections/auth/containers/AuthRouteContainer.tsx rename to src/core/client/admin/routes/configure/sections/auth/containers/AuthConfigRouteContainer.tsx index bf820ee50..428ca7d67 100644 --- a/src/core/client/admin/routes/configure/sections/auth/containers/AuthRouteContainer.tsx +++ b/src/core/client/admin/routes/configure/sections/auth/containers/AuthConfigRouteContainer.tsx @@ -2,14 +2,14 @@ import { FormApi } from "final-form"; import React from "react"; import { graphql } from "react-relay"; -import { AuthRouteContainerQueryResponse } from "talk-admin/__generated__/AuthRouteContainerQuery.graphql"; +import { AuthConfigRouteContainerQueryResponse } from "talk-admin/__generated__/AuthConfigRouteContainerQuery.graphql"; import { withRouteConfig } from "talk-framework/lib/router"; import { Delay, Spinner } from "talk-ui/components"; -import AuthContainer from ".//AuthContainer"; +import AuthConfigContainer from "./AuthConfigContainer"; interface Props { - data: AuthRouteContainerQueryResponse | null; + data: AuthConfigRouteContainerQueryResponse | null; form: FormApi; submitting?: boolean; } @@ -24,7 +24,7 @@ class AuthRouteContainer extends React.Component { ); } return ( - { const enhanced = withRouteConfig({ query: graphql` - query AuthRouteContainerQuery { + query AuthConfigRouteContainerQuery { settings { auth { - ...AuthContainer_auth + ...AuthConfigContainer_auth } } } diff --git a/src/core/client/admin/routes/configure/sections/general/components/ClosedStreamMessageConfig.tsx b/src/core/client/admin/routes/configure/sections/general/components/ClosedStreamMessageConfig.tsx new file mode 100644 index 000000000..328c399aa --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/components/ClosedStreamMessageConfig.tsx @@ -0,0 +1,61 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent, Suspense } from "react"; +import { Field } from "react-final-form"; + +import { + HorizontalGutter, + Spinner, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import Header from "../../../components/Header"; +import LazyMarkdown from "./LazyMarkdown"; + +interface Props { + disabled: boolean; +} + +const ClosedStreamMessageConfig: StatelessComponent = ({ disabled }) => ( + + +
+ } + > + Closed Stream Message +
+
+ } + > + + Write a message to appear after a story is closed for commenting. + + + + {({ input, meta }) => ( + <> + }> + + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + +
+); + +export default ClosedStreamMessageConfig; diff --git a/src/core/client/admin/routes/configure/sections/general/components/ClosingCommentStreamsConfig.tsx b/src/core/client/admin/routes/configure/sections/general/components/ClosingCommentStreamsConfig.tsx new file mode 100644 index 000000000..1ccd8d68a --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/components/ClosingCommentStreamsConfig.tsx @@ -0,0 +1,87 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; + +import { DURATION_UNIT, DurationField } from "talk-framework/components"; +import { + FormField, + HorizontalGutter, + InputLabel, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import { Field } from "react-final-form"; +import OnOffField from "talk-admin/routes/configure/components/OnOffField"; +import { + composeValidators, + required, + validateWholeNumberGreaterThan, +} from "talk-framework/lib/validation"; + +import Header from "../../../components/Header"; + +interface Props { + disabled: boolean; +} + +const ClosingCommentStreamsConfig: StatelessComponent = ({ + disabled, +}) => ( + + +
Closing Comment Streams
+
+ } + > + + Set comment streams to close after a defined period of time after a + story’s publication + + + + + Close Comments Automatically + + + + + + Close Comments After + + + + {({ input, meta }) => ( + <> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + + +
+); + +export default ClosingCommentStreamsConfig; diff --git a/src/core/client/admin/routes/configure/sections/general/components/CommentEditingConfig.css b/src/core/client/admin/routes/configure/sections/general/components/CommentEditingConfig.css new file mode 100644 index 000000000..d0a099916 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/components/CommentEditingConfig.css @@ -0,0 +1,3 @@ +.commentEditingTextInput { + width: 120px; +} diff --git a/src/core/client/admin/routes/configure/sections/general/components/CommentEditingConfig.tsx b/src/core/client/admin/routes/configure/sections/general/components/CommentEditingConfig.tsx new file mode 100644 index 000000000..66caffd14 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/components/CommentEditingConfig.tsx @@ -0,0 +1,78 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { DURATION_UNIT, DurationField } from "talk-framework/components"; +import { + composeValidators, + required, + validateWholeNumberGreaterThanOrEqual, +} from "talk-framework/lib/validation"; +import { + FormField, + HorizontalGutter, + InputLabel, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import Header from "../../../components/Header"; + +interface Props { + disabled: boolean; +} + +const CommentEditingConfig: StatelessComponent = ({ disabled }) => ( + + +
Comment Editing
+
+ } + > + + Set a limit on how long commenters have to edit their comments sitewide. + Edited comments are marked as (Edited) on the comment stream and the + moderation panel. + + + + + + Comment Edit Timeframe + + + {({ input, meta }) => ( + <> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + + +
+); + +export default CommentEditingConfig; diff --git a/src/core/client/admin/routes/configure/sections/general/components/CommentLengthConfig.css b/src/core/client/admin/routes/configure/sections/general/components/CommentLengthConfig.css new file mode 100644 index 000000000..cc1160d68 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/components/CommentLengthConfig.css @@ -0,0 +1,3 @@ +.commentLengthTextInput { + width: 150px; +} diff --git a/src/core/client/admin/routes/configure/sections/general/components/CommentLengthConfig.tsx b/src/core/client/admin/routes/configure/sections/general/components/CommentLengthConfig.tsx new file mode 100644 index 000000000..05f2c5e4c --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/components/CommentLengthConfig.tsx @@ -0,0 +1,163 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { + composeValidators, + createValidator, + validateWholeNumberGreaterThan, +} from "talk-framework/lib/validation"; +import { + FormField, + HorizontalGutter, + InputLabel, + TextField, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import Header from "../../../components/Header"; +import OnOffField from "../../../components/OnOffField"; + +import { formatEmpty, parseEmptyAsNull } from "talk-framework/lib/form"; +import styles from "./CommentLengthConfig.css"; + +const validateMaxLongerThanMin = createValidator( + (v, values) => + v === null || + values.charCount.min === null || + parseInt(v, 10) > parseInt(values.charCount.min, 10), + // tslint:disable-next-line:jsx-wrap-multiline + + Please enter a number longer than the minimum length + +); + +interface Props { + disabled: boolean; +} + +const CommentLengthConfig: StatelessComponent = ({ disabled }) => ( + + +
Comment Length
+
+ } + > + + Set a limit on the length of comments sitewide + + + + + + Limit Comment Length + + + + + + + + Minimum Comment Length + + + + {({ input, meta }) => ( + <> + + + Characters + + } + placeholder={"No limit"} + textAlignCenter + /> +
+ {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + + + + + + Maximum Comment Length + + + + {({ input, meta }) => ( + <> + + + Characters + + } + placeholder={"No limit"} + textAlignCenter + /> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + + +
+); + +export default CommentLengthConfig; diff --git a/src/core/client/admin/routes/configure/sections/general/components/GeneralConfig.tsx b/src/core/client/admin/routes/configure/sections/general/components/GeneralConfig.tsx new file mode 100644 index 000000000..e9910f508 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/components/GeneralConfig.tsx @@ -0,0 +1,56 @@ +import React, { StatelessComponent } from "react"; + +import { PropTypesOf } from "talk-framework/types"; +import { HorizontalGutter } from "talk-ui/components"; + +import ClosedStreamMessageConfigContainer from "../containers/ClosedStreamMessageConfigContainer"; +import ClosingCommentStreamsConfigContainer from "../containers/ClosingCommentStreamsConfigContainer"; +import CommentEditingConfigContainer from "../containers/CommentEditingConfigContainer"; +import CommentLengthConfigContainer from "../containers/CommentLengthConfigContainer"; +import GuidelinesConfigContainer from "../containers/GuidelinesConfigContainer"; + +interface Props { + disabled: boolean; + settings: PropTypesOf["settings"] & + PropTypesOf["settings"] & + PropTypesOf["settings"] & + PropTypesOf["settings"] & + PropTypesOf["settings"]; + onInitValues: (values: any) => void; +} + +const General: StatelessComponent = ({ + disabled, + settings, + onInitValues, +}) => ( + + + + + + + +); + +export default General; diff --git a/src/core/client/admin/routes/configure/sections/general/components/GuidelinesConfig.tsx b/src/core/client/admin/routes/configure/sections/general/components/GuidelinesConfig.tsx new file mode 100644 index 000000000..06a6f7654 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/components/GuidelinesConfig.tsx @@ -0,0 +1,81 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent, Suspense } from "react"; +import { Field } from "react-final-form"; + +import { ExternalLink } from "talk-framework/lib/i18n/components"; +import { + FormField, + HorizontalGutter, + InputLabel, + Spinner, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import OnOffField from "talk-admin/routes/configure/components/OnOffField"; +import Header from "../../../components/Header"; +import LazyMarkdown from "./LazyMarkdown"; + +interface Props { + disabled: boolean; +} + +const GuidelinesConfig: StatelessComponent = ({ disabled }) => ( + + +
Community Guidelines Summary
+
+ + + + + Show Community Guidelines Summary + + + + + + + + + Community Guidelines Summary + + + } + externalLink={} + > + + Write a summary of your community guidelines that will appear at the + top of each comment stream sitewide. Your summary can be formatted + using Markdown Syntax. More information on how to use Markdown can be + found here. + + + + + + {({ input, meta }) => ( + <> + }> + + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + +
+); + +export default GuidelinesConfig; diff --git a/src/core/client/admin/routes/configure/sections/general/components/LazyMarkdown.ts b/src/core/client/admin/routes/configure/sections/general/components/LazyMarkdown.ts new file mode 100644 index 000000000..5464f4f7e --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/components/LazyMarkdown.ts @@ -0,0 +1,7 @@ +import React from "react"; + +export function loadMarkdownEditor() { + return import("talk-framework/components/loadables/MarkdownEditor" /* webpackChunkName: "markdownEditor" */); +} + +export default React.lazy(loadMarkdownEditor); diff --git a/src/core/client/admin/routes/configure/sections/general/containers/ClosedStreamMessageConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/general/containers/ClosedStreamMessageConfigContainer.tsx new file mode 100644 index 000000000..c2a3f5879 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/containers/ClosedStreamMessageConfigContainer.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { ClosedStreamMessageConfigContainer_settings as SettingsData } from "talk-admin/__generated__/ClosedStreamMessageConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import ClosedStreamMessageConfig from "../components/ClosedStreamMessageConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class ClosedStreamMessageConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment ClosedStreamMessageConfigContainer_settings on Settings { + closedMessage + } + `, +})(ClosedStreamMessageConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/general/containers/ClosingCommentStreamsConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/general/containers/ClosingCommentStreamsConfigContainer.tsx new file mode 100644 index 000000000..31ba1d440 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/containers/ClosingCommentStreamsConfigContainer.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { ClosingCommentStreamsConfigContainer_settings as SettingsData } from "talk-admin/__generated__/ClosingCommentStreamsConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import ClosingCommentStreamsConfig from "../components/ClosingCommentStreamsConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class ClosingCommentStreamsConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment ClosingCommentStreamsConfigContainer_settings on Settings { + autoCloseStream + closedTimeout + } + `, +})(ClosingCommentStreamsConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/general/containers/CommentEditingConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/general/containers/CommentEditingConfigContainer.tsx new file mode 100644 index 000000000..527dc3b1d --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/containers/CommentEditingConfigContainer.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { CommentEditingConfigContainer_settings as SettingsData } from "talk-admin/__generated__/CommentEditingConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import CommentEditingConfig from "../components/CommentEditingConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class CommentEditingConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment CommentEditingConfigContainer_settings on Settings { + editCommentWindowLength + } + `, +})(CommentEditingConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/general/containers/CommentLengthConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/general/containers/CommentLengthConfigContainer.tsx new file mode 100644 index 000000000..29859ac5b --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/containers/CommentLengthConfigContainer.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { CommentLengthConfigContainer_settings as SettingsData } from "talk-admin/__generated__/CommentLengthConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import CommentLengthConfig from "../components/CommentLengthConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class CommentLengthConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment CommentLengthConfigContainer_settings on Settings { + charCount { + enabled + min + max + } + } + `, +})(CommentLengthConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/general/containers/GeneralConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/general/containers/GeneralConfigContainer.tsx new file mode 100644 index 000000000..7dd67c2fc --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/containers/GeneralConfigContainer.tsx @@ -0,0 +1,57 @@ +import { FormApi } from "final-form"; +import { RouteProps } from "found"; +import { merge } from "lodash"; +import React from "react"; +import { graphql } from "react-relay"; + +import { GeneralConfigContainer_settings as SettingsData } from "talk-admin/__generated__/GeneralConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import GeneralConfig from "../components/GeneralConfig"; + +interface Props { + form: FormApi; + submitting: boolean; + settings: SettingsData; +} + +class GeneralConfigContainer extends React.Component { + 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 = merge({}, this.initialValues, values); + }; + + public render() { + return ( + + ); + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment GeneralConfigContainer_settings on Settings { + ...GuidelinesConfigContainer_settings + ...CommentLengthConfigContainer_settings + ...CommentEditingConfigContainer_settings + ...ClosedStreamMessageConfigContainer_settings + ...ClosingCommentStreamsConfigContainer_settings + } + `, +})(GeneralConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/general/containers/GeneralConfigRouteContainer.tsx b/src/core/client/admin/routes/configure/sections/general/containers/GeneralConfigRouteContainer.tsx new file mode 100644 index 000000000..bf19e7569 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/containers/GeneralConfigRouteContainer.tsx @@ -0,0 +1,54 @@ +import { FormApi } from "final-form"; +import React from "react"; +import { graphql } from "react-relay"; + +import { GeneralConfigRouteContainerQueryResponse } from "talk-admin/__generated__/GeneralConfigRouteContainerQuery.graphql"; +import { withRouteConfig } from "talk-framework/lib/router"; +import { Delay, Spinner } from "talk-ui/components"; + +import { loadMarkdownEditor } from "../components/LazyMarkdown"; +import GeneralConfigContainer from "./GeneralConfigContainer"; + +interface Props { + data: GeneralConfigRouteContainerQueryResponse | null; + form: FormApi; + submitting: boolean; +} + +class GeneralConfigRouteContainer extends React.Component { + public render() { + if (!this.props.data) { + return ( + + + + ); + } + return ( + + ); + } +} + +const enhanced = withRouteConfig({ + getQuery: () => { + // Start prefetching markdown editor. + loadMarkdownEditor(); + + // Fetch graphql data. + return graphql` + query GeneralConfigRouteContainerQuery { + settings { + ...GeneralConfigContainer_settings + } + } + `; + }, + cacheConfig: { force: true }, +})(GeneralConfigRouteContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/general/containers/GuidelinesConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/general/containers/GuidelinesConfigContainer.tsx new file mode 100644 index 000000000..21b9fadca --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/general/containers/GuidelinesConfigContainer.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { GuidelinesConfigContainer_settings as SettingsData } from "talk-admin/__generated__/GuidelinesConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import GuidelinesConfig from "../components/GuidelinesConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class GuidelinesConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment GuidelinesConfigContainer_settings on Settings { + communityGuidelinesEnable + communityGuidelines + } + `, +})(GuidelinesConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/moderation/components/APIKeyField.tsx b/src/core/client/admin/routes/configure/sections/moderation/components/APIKeyField.tsx new file mode 100644 index 000000000..33bae0383 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/components/APIKeyField.tsx @@ -0,0 +1,54 @@ +import { Localized } from "fluent-react/compat"; +import { identity } from "lodash"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { Validator } from "talk-framework/lib/validation"; +import { FormField, InputLabel, TextField } from "talk-ui/components"; + +import ValidationMessage from "../../../components/ValidationMessage"; + +interface Props { + validate?: Validator; + name: string; + disabled: boolean; +} + +const APIKeyField: StatelessComponent = ({ + name, + disabled, + validate, +}) => ( + + + {({ input, meta }) => ( + <> + + + API Key + + + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + + +); + +export default APIKeyField; diff --git a/src/core/client/admin/routes/configure/sections/moderation/components/AkismetConfig.tsx b/src/core/client/admin/routes/configure/sections/moderation/components/AkismetConfig.tsx new file mode 100644 index 000000000..3580d7252 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/components/AkismetConfig.tsx @@ -0,0 +1,123 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { ExternalLink } from "talk-framework/lib/i18n/components"; +import { + composeValidators, + required, + validateURL, + Validator, +} from "talk-framework/lib/validation"; +import { + FormField, + HorizontalGutter, + InputLabel, + TextField, + Typography, +} from "talk-ui/components"; + +import ConfigurationSubHeader from "../../../components/ConfigurationSubHeader"; +import Header from "../../../components/Header"; +import OnOffField from "../../../components/OnOffField"; +import ValidationMessage from "../../../components/ValidationMessage"; +import APIKeyField from "./APIKeyField"; + +interface Props { + disabled: boolean; +} + +const AkismetConfig: StatelessComponent = ({ disabled }) => { + const validateWhenEnabled = (validator: Validator): Validator => ( + v, + values + ) => { + if (values.integrations.akismet.enabled) { + return validator(v, values); + } + return ""; + }; + return ( + + +
Akismet Spam Detection Filter
+
+ } + > + + Submitted comments are passed to the Akismet API for spam detection. + If a comment is determined to be spam, it will prompt the user, + indicating that the comment might be considered spam. If the user + continues after this point with the still spam-like comment, the + comment will be marked as containing spam, will not be published and + are placed in the Pending Queue for review by a moderator. If approved + by a moderator, the comment will be published. + + + + + + Spam Detection Filter + + + +
+ + } + > + + Note: You must add your active domain(s) in your Akismet account: + https://akismet.com/account/ + + +
+ + + + + + Site URL + + + + {({ input, meta }) => ( + <> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + + +
+ ); +}; + +export default AkismetConfig; diff --git a/src/core/client/admin/routes/configure/sections/moderation/components/ModerationConfig.spec.tsx b/src/core/client/admin/routes/configure/sections/moderation/components/ModerationConfig.spec.tsx new file mode 100644 index 000000000..4b8f32e18 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/components/ModerationConfig.spec.tsx @@ -0,0 +1,21 @@ +import { noop } from "lodash"; +import React from "react"; +import { createRenderer } from "react-test-renderer/shallow"; + +import { removeFragmentRefs } from "talk-framework/testHelpers"; +import { PropTypesOf } from "talk-framework/types"; + +import ModerationConfig from "./ModerationConfig"; + +const ModerationConfigN = removeFragmentRefs(ModerationConfig); + +it("renders correctly", () => { + const props: PropTypesOf = { + disabled: false, + settings: {}, + onInitValues: noop, + }; + const renderer = createRenderer(); + renderer.render(); + expect(renderer.getRenderOutput()).toMatchSnapshot(); +}); diff --git a/src/core/client/admin/routes/configure/sections/moderation/components/ModerationConfig.tsx b/src/core/client/admin/routes/configure/sections/moderation/components/ModerationConfig.tsx new file mode 100644 index 000000000..133ad8e8c --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/components/ModerationConfig.tsx @@ -0,0 +1,35 @@ +import React, { StatelessComponent } from "react"; + +import { PropTypesOf } from "talk-framework/types"; +import { HorizontalGutter } from "talk-ui/components"; + +import AkismetConfigContainer from "../containers/AkismetConfigContainer"; +import PerspectiveConfigContainer from "../containers/PerspectiveConfigContainer"; + +interface Props { + disabled: boolean; + settings: PropTypesOf["settings"] & + PropTypesOf["settings"]; + onInitValues: (values: any) => void; +} + +const ModerationConfig: StatelessComponent = ({ + disabled, + settings, + onInitValues, +}) => ( + + + + +); + +export default ModerationConfig; diff --git a/src/core/client/admin/routes/configure/sections/moderation/components/PerspectiveConfig.css b/src/core/client/admin/routes/configure/sections/moderation/components/PerspectiveConfig.css new file mode 100644 index 000000000..54cc0f1d0 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/components/PerspectiveConfig.css @@ -0,0 +1,3 @@ +.thresholdTextField { + width: 60px; +} diff --git a/src/core/client/admin/routes/configure/sections/moderation/components/PerspectiveConfig.tsx b/src/core/client/admin/routes/configure/sections/moderation/components/PerspectiveConfig.tsx new file mode 100644 index 000000000..5ce2e34e0 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/components/PerspectiveConfig.tsx @@ -0,0 +1,213 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; + +import { Field } from "react-final-form"; + +import { formatPercentage, parsePercentage } from "talk-framework/lib/form"; + +import { ExternalLink } from "talk-framework/lib/i18n/components"; +import { + composeValidators, + required, + validatePercentage, + validateURL, + Validator, +} from "talk-framework/lib/validation"; +import { + FormField, + HorizontalGutter, + InputDescription, + InputLabel, + TextField, + Typography, +} from "talk-ui/components"; + +import ConfigurationSubHeader from "../../../components/ConfigurationSubHeader"; +import Header from "../../../components/Header"; +import OnOffField from "../../../components/OnOffField"; +import PermissionField from "../../../components/PermissionField"; +import ValidationMessage from "../../../components/ValidationMessage"; +import APIKeyField from "./APIKeyField"; + +import styles from "./PerspectiveConfig.css"; + +/* TODO: use a common constants for both client and server. */ +const TOXICITY_DEFAULT = 80; + +interface Props { + disabled: boolean; +} + +const PerspectiveConfig: StatelessComponent = ({ disabled }) => { + const validateWhenEnabled = (validator: Validator): Validator => ( + v, + values + ) => { + if (values.integrations.perspective.enabled) { + return validator(v, values); + } + return ""; + }; + return ( + + +
Perspective Toxic Comment Filter
+
+ } + > + + Using the Perspective API, the Toxic Comment filter warns users when + comments exceed the predefined toxicity threshold. Comments with a + toxicity score above the threshold will not be published and are + placed in the Pending Queue for review by a moderator. If approved by + a moderator, the comment will be published. + + + + + + Spam Detection Filter + + + + + + + + Toxicity Threshold + + + + + This value can be set a percentage between 0 and 100. This number + represents the likelihood that a comment is toxic, according to + Perspective API. Defaults to $default. + + + + {({ input, meta }) => ( + <> + %} + placeholder={TOXICITY_DEFAULT.toString()} + textAlignCenter + /> + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + + + + + + + Allow Google to Store Comment Data + + + + + Stored comments will be used for future research and community model + building purposes to improve the API over time + + +
+ +
+
+
+ + } + > + + For additional information on how to set up the Perspective Toxic + Comment Filter please visit: + https://github.com/conversationai/perspectiveapi/blob/master/quickstart.md + + +
+ + + + + Custom Endpoint + + + + + By default the endpoint is set to $default. You may override this + here + + + + {({ input, meta }) => ( + <> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + + +
+ ); +}; + +export default PerspectiveConfig; diff --git a/src/core/client/admin/routes/configure/sections/moderation/components/__snapshots__/ModerationConfig.spec.tsx.snap b/src/core/client/admin/routes/configure/sections/moderation/components/__snapshots__/ModerationConfig.spec.tsx.snap new file mode 100644 index 000000000..ab24a8aae --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/components/__snapshots__/ModerationConfig.spec.tsx.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + + + + +`; diff --git a/src/core/client/admin/routes/configure/sections/moderation/containers/AkismetConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/moderation/containers/AkismetConfigContainer.tsx new file mode 100644 index 000000000..2e4b95752 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/containers/AkismetConfigContainer.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { AkismetConfigContainer_settings as SettingsData } from "talk-admin/__generated__/AkismetConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import AkismetConfig from "../components/AkismetConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class AkismetConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment AkismetConfigContainer_settings on Settings { + integrations { + akismet { + enabled + key + site + } + } + } + `, +})(AkismetConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/moderation/containers/ModerationConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/moderation/containers/ModerationConfigContainer.tsx new file mode 100644 index 000000000..fcd44eea5 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/containers/ModerationConfigContainer.tsx @@ -0,0 +1,54 @@ +import { FormApi } from "final-form"; +import { RouteProps } from "found"; +import { merge } from "lodash"; +import React from "react"; +import { graphql } from "react-relay"; + +import { ModerationConfigContainer_settings as SettingsData } from "talk-admin/__generated__/ModerationConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import ModerationConfig from "../components/ModerationConfig"; + +interface Props { + form: FormApi; + submitting: boolean; + settings: SettingsData; +} + +class ModerationConfigContainer extends React.Component { + 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 = merge({}, this.initialValues, values); + }; + + public render() { + return ( + + ); + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment ModerationConfigContainer_settings on Settings { + ...AkismetConfigContainer_settings + ...PerspectiveConfigContainer_settings + } + `, +})(ModerationConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/moderation/containers/ModerationConfigRouteContainer.tsx b/src/core/client/admin/routes/configure/sections/moderation/containers/ModerationConfigRouteContainer.tsx new file mode 100644 index 000000000..ca5de6d2c --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/containers/ModerationConfigRouteContainer.tsx @@ -0,0 +1,47 @@ +import { FormApi } from "final-form"; +import React from "react"; +import { graphql } from "react-relay"; + +import { ModerationConfigRouteContainerQueryResponse } from "talk-admin/__generated__/ModerationConfigRouteContainerQuery.graphql"; +import { withRouteConfig } from "talk-framework/lib/router"; +import { Delay, Spinner } from "talk-ui/components"; + +import ModerationConfigContainer from "./ModerationConfigContainer"; + +interface Props { + data: ModerationConfigRouteContainerQueryResponse | null; + form: FormApi; + submitting: boolean; +} + +class ModerationConfigRouteContainer extends React.Component { + public render() { + if (!this.props.data) { + return ( + + + + ); + } + return ( + + ); + } +} + +const enhanced = withRouteConfig({ + query: graphql` + query ModerationConfigRouteContainerQuery { + settings { + ...ModerationConfigContainer_settings + } + } + `, + cacheConfig: { force: true }, +})(ModerationConfigRouteContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/moderation/containers/PerspectiveConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/moderation/containers/PerspectiveConfigContainer.tsx new file mode 100644 index 000000000..ed0479eb9 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/moderation/containers/PerspectiveConfigContainer.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { PerspectiveConfigContainer_settings as SettingsData } from "talk-admin/__generated__/PerspectiveConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import PerspectiveConfig from "../components/PerspectiveConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class PerspectiveConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment PerspectiveConfigContainer_settings on Settings { + integrations { + perspective { + enabled + endpoint + key + threshold + doNotStore + } + } + } + `, +})(PerspectiveConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/organization/components/OrganizationConfig.tsx b/src/core/client/admin/routes/configure/sections/organization/components/OrganizationConfig.tsx new file mode 100644 index 000000000..dab3870b0 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/organization/components/OrganizationConfig.tsx @@ -0,0 +1,35 @@ +import React, { StatelessComponent } from "react"; + +import { PropTypesOf } from "talk-framework/types"; +import { HorizontalGutter } from "talk-ui/components"; + +import OrganizationContactEmailConfigContainer from "../containers/OrganizationContactEmailConfigContainer"; +import OrganizationNameConfigContainer from "../containers/OrganizationNameConfigContainer"; + +interface Props { + disabled: boolean; + settings: PropTypesOf["settings"] & + PropTypesOf["settings"]; + onInitValues: (values: any) => void; +} + +const OrganizationConfig: StatelessComponent = ({ + disabled, + settings, + onInitValues, +}) => ( + + + + +); + +export default OrganizationConfig; diff --git a/src/core/client/admin/routes/configure/sections/organization/components/OrganizationContactEmailConfig.tsx b/src/core/client/admin/routes/configure/sections/organization/components/OrganizationContactEmailConfig.tsx new file mode 100644 index 000000000..450a89ddc --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/organization/components/OrganizationContactEmailConfig.tsx @@ -0,0 +1,66 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { required } from "talk-framework/lib/validation"; +import { + FormField, + HorizontalGutter, + TextField, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import Header from "../../../components/Header"; + +interface Props { + disabled: boolean; +} + +const OrganizationNameConfig: StatelessComponent = ({ disabled }) => ( + + + +
+ } + > + Organization Email +
+
+ } + > + This E-Mail will be used + + + {({ input, meta }) => ( + <> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + +
+
+); + +export default OrganizationNameConfig; diff --git a/src/core/client/admin/routes/configure/sections/organization/components/OrganizationNameConfig.tsx b/src/core/client/admin/routes/configure/sections/organization/components/OrganizationNameConfig.tsx new file mode 100644 index 000000000..391ed75cf --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/organization/components/OrganizationNameConfig.tsx @@ -0,0 +1,69 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { required } from "talk-framework/lib/validation"; +import { + FormField, + HorizontalGutter, + TextField, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import Header from "../../../components/Header"; + +interface Props { + disabled: boolean; +} + +const OrganizationNameConfig: StatelessComponent = ({ disabled }) => ( + + + +
+ } + > + Organization Name +
+
+ } + > + + Your organization name will appear on emails sent by Talk to your + community and organization members + + + + {({ input, meta }) => ( + <> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} + + )} + +
+
+); + +export default OrganizationNameConfig; diff --git a/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationConfigContainer.tsx new file mode 100644 index 000000000..ac7897be9 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationConfigContainer.tsx @@ -0,0 +1,54 @@ +import { FormApi } from "final-form"; +import { RouteProps } from "found"; +import { merge } from "lodash"; +import React from "react"; +import { graphql } from "react-relay"; + +import { OrganizationConfigContainer_settings as SettingsData } from "talk-admin/__generated__/OrganizationConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import OrganizationConfig from "../components/OrganizationConfig"; + +interface Props { + form: FormApi; + submitting: boolean; + settings: SettingsData; +} + +class OrganizationConfigContainer extends React.Component { + 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 = merge({}, this.initialValues, values); + }; + + public render() { + return ( + + ); + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment OrganizationConfigContainer_settings on Settings { + ...OrganizationNameConfigContainer_settings + ...OrganizationContactEmailConfigContainer_settings + } + `, +})(OrganizationConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationContactEmailConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationContactEmailConfigContainer.tsx new file mode 100644 index 000000000..9de8e0716 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationContactEmailConfigContainer.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { OrganizationContactEmailConfigContainer_settings as SettingsData } from "talk-admin/__generated__/OrganizationContactEmailConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import OrganizationContactEmailConfig from "../components/OrganizationContactEmailConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class OrganizationContactEmailConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment OrganizationContactEmailConfigContainer_settings on Settings { + organizationContactEmail + } + `, +})(OrganizationContactEmailConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationNameConfigContainer.tsx b/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationNameConfigContainer.tsx new file mode 100644 index 000000000..3d5183339 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationNameConfigContainer.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { OrganizationNameConfigContainer_settings as SettingsData } from "talk-admin/__generated__/OrganizationNameConfigContainer_settings.graphql"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import OrganizationNameConfig from "../components/OrganizationNameConfig"; + +interface Props { + settings: SettingsData; + onInitValues: (values: SettingsData) => void; + disabled: boolean; +} + +class OrganizationNameConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.settings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + settings: graphql` + fragment OrganizationNameConfigContainer_settings on Settings { + organizationName + } + `, +})(OrganizationNameConfigContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationRouteContainer.tsx b/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationRouteContainer.tsx new file mode 100644 index 000000000..501c7ac89 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/organization/containers/OrganizationRouteContainer.tsx @@ -0,0 +1,47 @@ +import { FormApi } from "final-form"; +import React from "react"; +import { graphql } from "react-relay"; + +import { OrganizationRouteContainerQueryResponse } from "talk-admin/__generated__/OrganizationRouteContainerQuery.graphql"; +import { withRouteConfig } from "talk-framework/lib/router"; +import { Delay, Spinner } from "talk-ui/components"; + +import OrganizationConfigContainer from "./OrganizationConfigContainer"; + +interface Props { + data: OrganizationRouteContainerQueryResponse | null; + form: FormApi; + submitting: boolean; +} + +class OrganizationRouteContainer extends React.Component { + public render() { + if (!this.props.data) { + return ( + + + + ); + } + return ( + + ); + } +} + +const enhanced = withRouteConfig({ + query: graphql` + query OrganizationRouteContainerQuery { + settings { + ...OrganizationConfigContainer_settings + } + } + `, + cacheConfig: { force: true }, +})(OrganizationRouteContainer); + +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/wordList/components/BannedWordListConfig.css b/src/core/client/admin/routes/configure/sections/wordList/components/BannedWordListConfig.css new file mode 100644 index 000000000..f1ee4a568 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/wordList/components/BannedWordListConfig.css @@ -0,0 +1,3 @@ +.textArea { + margin-top: var(--spacing-unit); +} diff --git a/src/core/client/admin/routes/configure/sections/wordList/components/BannedWordListConfig.tsx b/src/core/client/admin/routes/configure/sections/wordList/components/BannedWordListConfig.tsx new file mode 100644 index 000000000..c4d347b3e --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/wordList/components/BannedWordListConfig.tsx @@ -0,0 +1,63 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; + +import { ExternalLink } from "talk-framework/lib/i18n/components"; +import { + FormField, + HorizontalGutter, + InputDescription, + InputLabel, + Typography, +} from "talk-ui/components"; + +import Header from "../../../components/Header"; +import WordListTextArea from "./WordListTextArea"; + +import styles from "./BannedWordListConfig.css"; + +interface Props { + disabled: boolean; +} + +const BannedWordListConfig: StatelessComponent = ({ disabled }) => ( + + +
Banned Words and Phrases
+
+ }> + + Comments containing a word or phrase in the banned words list are + automatically rejected and are not published. + + + + + + + Banned Word List + + + } + externalLink={} + > + + Separate banned words or phrases with a new line. Attempting to copy + and paste a comma separated list? Learn how to convert your list to a + new line separated list. + + +
+ +
+
+
+); + +export default BannedWordListConfig; diff --git a/src/core/client/admin/routes/configure/sections/wordList/components/SuspectWordListConfig.css b/src/core/client/admin/routes/configure/sections/wordList/components/SuspectWordListConfig.css new file mode 100644 index 000000000..f1ee4a568 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/wordList/components/SuspectWordListConfig.css @@ -0,0 +1,3 @@ +.textArea { + margin-top: var(--spacing-unit); +} diff --git a/src/core/client/admin/routes/configure/sections/wordList/components/SuspectWordListConfig.tsx b/src/core/client/admin/routes/configure/sections/wordList/components/SuspectWordListConfig.tsx new file mode 100644 index 000000000..bf2720f49 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/wordList/components/SuspectWordListConfig.tsx @@ -0,0 +1,64 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent } from "react"; + +import { ExternalLink } from "talk-framework/lib/i18n/components"; +import { + FormField, + HorizontalGutter, + InputDescription, + InputLabel, + Typography, +} from "talk-ui/components"; + +import Header from "../../../components/Header"; +import WordListTextArea from "./WordListTextArea"; + +import styles from "./SuspectWordListConfig.css"; + +interface Props { + disabled: boolean; +} + +const SuspectWordListConfig: StatelessComponent = ({ disabled }) => ( + + +
Suspect Words and Phrases
+
+ }> + + Comments containing a word or phrase in the Suspect Words List are + placed into the Reported Queue for moderator review and are published + (if comments are not pre-moderated). + + + + + + + Suspect Word List + + + } + externalLink={} + > + + Separate suspect words or phrases with a new line. Attempting to copy + and paste a comma separated list? Learn how to convert your list to a + new line separated list. + + +
+ +
+
+
+); + +export default SuspectWordListConfig; diff --git a/src/core/client/admin/routes/configure/sections/wordList/components/WordListConfig.tsx b/src/core/client/admin/routes/configure/sections/wordList/components/WordListConfig.tsx new file mode 100644 index 000000000..181ff8c04 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/wordList/components/WordListConfig.tsx @@ -0,0 +1,35 @@ +import React, { StatelessComponent } from "react"; + +import { PropTypesOf } from "talk-framework/types"; +import { HorizontalGutter } from "talk-ui/components"; + +import BannedWordListConfigContainer from "../containers/BannedWordListConfigContainer"; +import SuspectWordListConfigContainer from "../containers/SuspectWordListConfigContainer"; + +interface Props { + disabled: boolean; + settings: PropTypesOf["settings"] & + PropTypesOf["settings"]; + onInitValues: (values: any) => void; +} + +const WordListConfig: StatelessComponent = ({ + disabled, + settings, + onInitValues, +}) => ( + + + + +); + +export default WordListConfig; diff --git a/src/core/client/admin/routes/configure/sections/wordList/components/WordListTextArea.css b/src/core/client/admin/routes/configure/sections/wordList/components/WordListTextArea.css new file mode 100644 index 000000000..29c4b32fa --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/wordList/components/WordListTextArea.css @@ -0,0 +1,6 @@ +.textArea { + width: 100%; + box-sizing: border-box; + height: calc(23 * var(--spacing-unit)); + padding: calc(0.5 * var(--spacing-unit)); +} diff --git a/src/core/client/admin/routes/configure/sections/wordList/components/WordListTextArea.tsx b/src/core/client/admin/routes/configure/sections/wordList/components/WordListTextArea.tsx new file mode 100644 index 000000000..a55188828 --- /dev/null +++ b/src/core/client/admin/routes/configure/sections/wordList/components/WordListTextArea.tsx @@ -0,0 +1,61 @@ +import cn from "classnames"; +import React, { StatelessComponent } from "react"; +import { Field } from "react-final-form"; + +import { + formatNewLineDelimitedString, + parseNewLineDelimitedString, +} from "talk-framework/lib/form"; +import { Validator } from "talk-framework/lib/validation"; + +import ValidationMessage from "../../../components/ValidationMessage"; + +import styles from "./WordListTextArea.css"; + +interface Props { + className?: string; + validate?: Validator; + id?: string; + name: string; + disabled: boolean; +} + +const WordListTextArea: StatelessComponent = ({ + id, + name, + disabled, + validate, + className, +}) => ( + + {({ input, meta }) => ( + <> +