From 9eb5afbb2b977847cc331a2695c586f5db4d4995 Mon Sep 17 00:00:00 2001 From: Kiwi Date: Mon, 18 Mar 2019 22:07:52 +0100 Subject: [PATCH] [CORL-159, CORL-160] Stream Config Tab (#2219) * feat: Implement stream configuration tab * feat: split profile & configure into separate bundles * chore: better role logic * fix+chore: add test cases, implement expectAndFail, refactor tests * chore: add some comments * chore: Update src/core/client/framework/lib/form/helpers.tsx Co-Authored-By: cvle * feat: support new graphql mutations/schema * fix: ci fixes * fix: improvement to revision loading * fix: updated some tests * fix: adapt client to changes * fix: remove obsolote isClosed in UpdateStory * ci: increase no_output_timeout for build --- .circleci/config.yml | 18 +- .nvmrc | 2 + Dockerfile | 19 +- package-lock.json | 150 +++++-- package.json | 10 +- .../configure/components/Configure.spec.tsx | 3 +- .../routes/configure/components/Configure.tsx | 7 +- .../containers/ConfigureContainer.tsx | 118 ++--- .../containers/NavigationWarningContainer.tsx | 43 ++ .../components/ConfigBoxWithToggleField.tsx | 5 +- .../auth/components/TargetFilterField.tsx | 7 +- .../auth/containers/AuthConfigContainer.tsx | 8 +- .../components/ClosedStreamMessageConfig.tsx | 2 +- .../ClosingCommentStreamsConfig.tsx | 4 +- .../ClosedStreamMessageConfigContainer.tsx | 4 +- .../ClosingCommentStreamsConfigContainer.tsx | 6 +- .../OrganizationContactEmailConfig.tsx | 4 +- .../components/OrganizationNameConfig.tsx | 4 +- ...rganizationContactEmailConfigContainer.tsx | 4 +- .../OrganizationNameConfigContainer.tsx | 4 +- .../routes/configure/submitHook/index.ts | 9 - .../addEmailAddress.spec.tsx.snap | 77 ---- .../createUsername.spec.tsx.snap | 58 --- .../admin/test/auth/addEmailAddress.spec.tsx | 16 +- .../admin/test/auth/createPassword.spec.tsx | 2 +- .../admin/test/auth/createUsername.spec.tsx | 9 +- .../__snapshots__/general.spec.tsx.snap | 18 +- .../__snapshots__/organization.spec.tsx.snap | 12 +- .../admin/test/configure/advanced.spec.tsx | 17 +- .../client/admin/test/configure/auth.spec.tsx | 10 +- .../admin/test/configure/general.spec.tsx | 33 +- .../admin/test/configure/moderation.spec.tsx | 17 +- .../test/configure/organization.spec.tsx | 17 +- .../admin/test/configure/wordList.spec.tsx | 13 +- src/core/client/admin/test/fixtures.ts | 14 +- .../admin/test/moderate/moderate.spec.tsx | 36 +- .../addEmailAddress.spec.tsx.snap | 146 ------ .../createUsername.spec.tsx.snap | 58 --- .../client/auth/test/addEmailAddress.spec.tsx | 16 +- .../client/auth/test/createPassword.spec.tsx | 2 +- .../client/auth/test/createUsername.spec.tsx | 9 +- src/core/client/framework/helpers/getStory.ts | 8 + .../framework/helpers/getStorySettings.ts | 15 + src/core/client/framework/helpers/index.ts | 2 + .../framework/lib/form/FormInitializer.tsx | 44 ++ .../lib/form}/SubmitHookContext.tsx | 0 .../framework/lib/form/SubmitHookHandler.tsx | 85 ++++ .../lib/{form.tsx => form/helpers.tsx} | 1 + src/core/client/framework/lib/form/index.tsx | 5 + .../lib/form}/withSubmitHookContext.ts | 0 .../lib/relay/commitMutationPromise.ts | 6 +- src/core/client/framework/rest/install.ts | 8 +- .../install/containers/WizardContainer.tsx | 8 +- .../install/mutations/InstallMutation.ts | 6 +- .../containers/AddOrganizationContainer.tsx | 9 +- .../containers/CreateYourAccountContainer.tsx | 9 +- src/core/client/stream/components/App.tsx | 4 + src/core/client/stream/components/TabBar.tsx | 18 +- .../__snapshots__/App.spec.tsx.snap | 6 + .../stream/containers/TabBarContainer.tsx | 11 + src/core/client/stream/helpers/index.ts | 2 +- .../stream/helpers/isRolePriviledged.ts | 5 - .../client/stream/helpers/roleIsAtLeast.ts | 17 + .../stream/mutations/CreateCommentMutation.ts | 17 +- .../mutations/CreateCommentReplyMutation.ts | 17 +- .../mutations/UpdateStorySettingsMutation.ts | 47 ++ src/core/client/stream/mutations/index.ts | 5 + .../client/stream/queries/TabBarQuery.tsx | 10 +- .../stream/tabs/comments/components/RTE.tsx | 5 +- .../PostCommentFormContainer.spec.tsx | 16 +- .../containers/PostCommentFormContainer.tsx | 8 +- .../ReplyCommentFormContainer.spec.tsx | 9 +- .../containers/ReplyCommentFormContainer.tsx | 6 +- .../tabs/configure/components/Configure.tsx | 24 + .../components/ConfigureCommentStream.css | 3 + .../components/ConfigureCommentStream.tsx | 77 ++++ .../configure/components/PremodConfig.tsx | 48 ++ .../components/PremodLinksConfig.tsx | 41 ++ .../configure/components/ToggleConfig.css | 4 + .../configure/components/ToggleConfig.tsx | 27 ++ .../components/WidthLimitedDescription.css | 3 + .../components/WidthLimitedDescription.tsx | 20 + .../ConfigureCommentStreamContainer.tsx | 57 +++ .../containers/ConfigureContainer.tsx | 46 ++ .../containers/PremodConfigContainer.tsx | 35 ++ .../containers/PremodLinksConfigContainer.tsx | 35 ++ .../tabs/configure/queries/ConfigureQuery.tsx | 106 +++++ .../profile/components/HistoryComment.css | 4 - .../profile/components/HistoryComment.tsx | 4 - .../HistoryComment.spec.tsx.snap | 7 - .../profile/containers/ProfileContainer.tsx | 4 +- .../tabs/profile/queries/ProfileQuery.tsx | 27 +- .../closedOrDisabledCommentStream.spec.tsx | 2 +- .../stream/test/comments/editComment.spec.tsx | 2 +- .../stream/test/comments/postComment.spec.tsx | 6 +- .../test/comments/postLocalReply.spec.tsx | 2 +- .../stream/test/comments/postReply.spec.tsx | 6 +- .../stream/test/comments/reaction.spec.tsx | 6 +- .../test/comments/renderStream.spec.tsx | 2 +- .../test/comments/reportComment.spec.tsx | 6 +- .../stream/test/comments/sortStream.spec.tsx | 8 +- .../renderConfigure.spec.tsx.snap | 169 +++++++ .../client/stream/test/configure/create.ts | 14 + .../test/configure/renderConfigure.spec.tsx | 45 ++ .../configure/streamConfiguration.spec.tsx | 130 ++++++ src/core/client/stream/test/fixtures.ts | 18 +- src/core/client/test/expectAndFail.tsx | 56 +++ src/core/client/test/setupTestFramework.ts | 7 + .../ui/components/CheckBox/CheckBox.css | 8 +- src/core/server/app/handlers/api/install.ts | 18 +- .../server/graph/tenant/mutators/Stories.ts | 23 +- .../server/graph/tenant/resolvers/Mutation.ts | 12 + .../server/graph/tenant/resolvers/Story.ts | 10 +- .../graph/tenant/resolvers/StorySettings.ts | 20 + .../server/graph/tenant/resolvers/index.ts | 2 + .../server/graph/tenant/schema/schema.graphql | 421 +++++++++++++----- src/core/server/models/settings.ts | 105 ++--- src/core/server/models/story/index.ts | 98 +++- src/core/server/models/tenant.ts | 50 +-- .../comments/pipeline/phases/commentLength.ts | 7 +- .../pipeline/phases/commentingDisabled.ts | 4 +- .../comments/pipeline/phases/detectLinks.ts | 4 +- .../comments/pipeline/phases/preModerate.ts | 4 +- .../pipeline/phases/storyClosed.spec.ts | 18 +- src/core/server/services/stories/index.ts | 33 +- src/locales/en-US/stream.ftl | 16 + src/types/expectAndFail.d.ts | 2 + src/types/found-relay.d.ts | 4 +- 128 files changed, 2249 insertions(+), 1081 deletions(-) create mode 100644 .nvmrc create mode 100644 src/core/client/admin/routes/configure/containers/NavigationWarningContainer.tsx delete mode 100644 src/core/client/admin/routes/configure/submitHook/index.ts create mode 100644 src/core/client/framework/helpers/getStory.ts create mode 100644 src/core/client/framework/helpers/getStorySettings.ts create mode 100644 src/core/client/framework/lib/form/FormInitializer.tsx rename src/core/client/{admin/routes/configure/submitHook => framework/lib/form}/SubmitHookContext.tsx (100%) create mode 100644 src/core/client/framework/lib/form/SubmitHookHandler.tsx rename src/core/client/framework/lib/{form.tsx => form/helpers.tsx} (96%) create mode 100644 src/core/client/framework/lib/form/index.tsx rename src/core/client/{admin/routes/configure/submitHook => framework/lib/form}/withSubmitHookContext.ts (100%) delete mode 100644 src/core/client/stream/helpers/isRolePriviledged.ts create mode 100644 src/core/client/stream/helpers/roleIsAtLeast.ts create mode 100644 src/core/client/stream/mutations/UpdateStorySettingsMutation.ts create mode 100644 src/core/client/stream/tabs/configure/components/Configure.tsx create mode 100644 src/core/client/stream/tabs/configure/components/ConfigureCommentStream.css create mode 100644 src/core/client/stream/tabs/configure/components/ConfigureCommentStream.tsx create mode 100644 src/core/client/stream/tabs/configure/components/PremodConfig.tsx create mode 100644 src/core/client/stream/tabs/configure/components/PremodLinksConfig.tsx create mode 100644 src/core/client/stream/tabs/configure/components/ToggleConfig.css create mode 100644 src/core/client/stream/tabs/configure/components/ToggleConfig.tsx create mode 100644 src/core/client/stream/tabs/configure/components/WidthLimitedDescription.css create mode 100644 src/core/client/stream/tabs/configure/components/WidthLimitedDescription.tsx create mode 100644 src/core/client/stream/tabs/configure/containers/ConfigureCommentStreamContainer.tsx create mode 100644 src/core/client/stream/tabs/configure/containers/ConfigureContainer.tsx create mode 100644 src/core/client/stream/tabs/configure/containers/PremodConfigContainer.tsx create mode 100644 src/core/client/stream/tabs/configure/containers/PremodLinksConfigContainer.tsx create mode 100644 src/core/client/stream/tabs/configure/queries/ConfigureQuery.tsx create mode 100644 src/core/client/stream/test/configure/__snapshots__/renderConfigure.spec.tsx.snap create mode 100644 src/core/client/stream/test/configure/create.ts create mode 100644 src/core/client/stream/test/configure/renderConfigure.spec.tsx create mode 100644 src/core/client/stream/test/configure/streamConfiguration.spec.tsx create mode 100644 src/core/client/test/expectAndFail.tsx create mode 100644 src/core/server/graph/tenant/resolvers/StorySettings.ts create mode 100644 src/types/expectAndFail.d.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index 4fe959c01..ffc60adb7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -107,6 +107,7 @@ jobs: - run: name: Build command: npm run build + no_output_timeout: 20m - run: name: Verify Bundle Size command: npx bundlesize @@ -120,14 +121,18 @@ jobs: # docker_tests will test that the docker build process completes. docker_tests: - <<: *job_defaults + docker: + - image: node:10-alpine + environment: + NODE_ENV: development steps: - checkout - - setup_remote_docker - run: - name: Build - command: docker build -t coralproject/talk:next --build-arg REVISION_HASH=${CIRCLE_SHA1} . - no_output_timeout: 20m + name: Update Image Dependancies + command: apk --no-cache add git python + - run: + name: Install NodeJS Dependancies + command: npm install # release_docker will build and push the Docker image. release_docker: @@ -140,7 +145,8 @@ jobs: command: docker login -u $DOCKER_USER -p $DOCKER_PASS - run: name: Build - command: docker build -t coralproject/talk:next --build-arg REVISION_HASH=${CIRCLE_SHA1} . + command: docker build -t coralproject/talk:next --build-arg REVISION_HASH=$CIRCLE_SHA1 . + no_output_timeout: 30m - run: name: Push command: docker push coralproject/talk:next diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..021ea30c0 --- /dev/null +++ b/.nvmrc @@ -0,0 +1,2 @@ +10 + diff --git a/Dockerfile b/Dockerfile index 866f3f22e..971a262d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,30 +13,21 @@ ENV NODE_ENV production # Bundle application source. COPY . /usr/src/app +# Store the current git revision. +ARG REVISION_HASH +RUN mkdir -p dist/core/common/__generated__ && \ + echo "{\"revision\": \"${REVISION_HASH}\"}" > dist/core/common/__generated__/revision.json + # Install build static assets and clear caches. RUN NODE_ENV=development npm install && \ npm run generate && \ npm run build && \ npm prune --production -FROM node:10-alpine - -# Create app directory -RUN mkdir -p /usr/src/app -WORKDIR /usr/src/app - -# Copy the compiled source into the new stage. -COPY --from=0 /usr/src/app . - # Setup the environment ENV PATH /usr/src/app/bin:$PATH ENV PORT 5000 EXPOSE 5000 ENV NODE_ENV production -# Store the current git revision. -ARG REVISION_HASH -RUN mkdir dist/core/common/__generated__ && \ - echo "{\"revision\": \"${REVISION_HASH}\"}" > dist/core/common/__generated__/revision.json - CMD ["npm", "run", "start"] diff --git a/package-lock.json b/package-lock.json index bd6b6810e..45717174e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2050,7 +2050,7 @@ }, "@types/accepts": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "resolved": "http://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", "requires": { "@types/node": "*" @@ -2565,9 +2565,9 @@ "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" }, "@types/react": { - "version": "16.7.20", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.7.20.tgz", - "integrity": "sha512-Qd5RWkwl6SL7R2XzLk/cicjVQm1Mhc6HqXY5Ei4pWd1Vi8Fkbd5O0sA398x8fRSTPAuHdDYD9nrWmJMYTJI0vQ==", + "version": "16.8.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.7.tgz", + "integrity": "sha512-0xbkIyrDNKUn4IJVf8JaCn+ucao/cq6ZB8O6kSzhrJub1cVSqgTArtG0qCfdERWKMEIvUbrwLXeQMqWEsyr9dA==", "dev": true, "requires": { "@types/prop-types": "*", @@ -11493,21 +11493,21 @@ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, "found": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/found/-/found-0.3.14.tgz", - "integrity": "sha512-Z4Ag0L8whIJwVf0MyNTkx9q3ekmbqK5RkCvlcVU7Dn3ruKfKSxbPReUiQi7mWGMTAut2JnkwiobSD/FQRhk9Vw==", + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/found/-/found-0.3.21.tgz", + "integrity": "sha512-iaeGVTBirrTAravFPcIfWPuJHIaFNqnJ87AMvnpB4Ig8RTB97C4PipVopmSxK+lLeMdEybUwaW+BhNDhLPEUSw==", "requires": { "babel-runtime": "^6.26.0", "farce": "^0.2.6", "is-promise": "^2.1.0", - "lodash": "^4.17.5", + "lodash": "^4.17.11", "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.1", - "prop-types-extra": "^1.0.1", - "react-redux": "^5.0.7", + "prop-types": "^15.6.2", + "prop-types-extra": "^1.1.0", + "react-redux": "^5.1.1", "react-static-container": "^1.0.2", - "redux": "^4.0.0", - "warning": "^3.0.0" + "redux": "^4.0.1", + "warning": "^4.0.2" }, "dependencies": { "isarray": { @@ -11515,6 +11515,19 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "path-to-regexp": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", @@ -11523,10 +11536,19 @@ "isarray": "0.0.1" } }, + "redux": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz", + "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==", + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "requires": { "loose-envify": "^1.0.0" } @@ -13288,7 +13310,8 @@ "hoist-non-react-statics": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==", + "dev": true }, "home-or-tmp": { "version": "2.0.0", @@ -16852,7 +16875,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -17119,7 +17142,8 @@ "lodash-es": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", - "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" + "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==", + "dev": true }, "lodash._reinterpolate": { "version": "3.0.0", @@ -19442,7 +19466,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -22919,14 +22943,14 @@ } }, "react": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.8.1.tgz", - "integrity": "sha512-wLw5CFGPdo7p/AgteFz7GblI2JPOos0+biSoxf1FPsGxWQZdN/pj6oToJs1crn61DL3Ln7mN86uZ4j74p31ELQ==", + "version": "16.8.4", + "resolved": "https://registry.npmjs.org/react/-/react-16.8.4.tgz", + "integrity": "sha512-0GQ6gFXfUH7aZcjGVymlPOASTuSjlQL4ZtVC5YKH+3JL6bBLCVO21DknzmaPlI90LN253ojj02nsapy+j7wIjg==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.13.1" + "scheduler": "^0.13.4" } }, "react-adopt": { @@ -23231,14 +23255,14 @@ } }, "react-dom": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.1.tgz", - "integrity": "sha512-N74IZUrPt6UiDjXaO7UbDDFXeUXnVhZzeRLy/6iqqN1ipfjrhR60Bp5NuBK+rv3GMdqdIuwIl22u1SYwf330bg==", + "version": "16.8.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.4.tgz", + "integrity": "sha512-Ob2wK7XG2tUDt7ps7LtLzGYYB6DXMCLj0G5fO6WeEICtT4/HdpOi7W/xLzZnR6RCG1tYza60nMdqtxzA8FaPJQ==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.13.1" + "scheduler": "^0.13.4" } }, "react-emotion": { @@ -23285,8 +23309,7 @@ "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", - "dev": true + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "react-lightweight-tooltip": { "version": "1.0.0", @@ -23356,16 +23379,45 @@ } }, "react-redux": { - "version": "5.0.7", - "resolved": "http://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz", - "integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.1.tgz", + "integrity": "sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==", "requires": { - "hoist-non-react-statics": "^2.5.0", - "invariant": "^2.0.0", - "lodash": "^4.17.5", - "lodash-es": "^4.17.5", + "@babel/runtime": "^7.1.2", + "hoist-non-react-statics": "^3.1.0", + "invariant": "^2.2.4", "loose-envify": "^1.1.0", - "prop-types": "^15.6.0" + "prop-types": "^15.6.1", + "react-is": "^16.6.0", + "react-lifecycles-compat": "^3.0.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz", + "integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==", + "requires": { + "regenerator-runtime": "^0.12.0" + } + }, + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, + "react-is": { + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz", + "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==" + }, + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" + } } }, "react-relay": { @@ -23457,21 +23509,21 @@ "integrity": "sha512-rxlZtZk5t6Y3gqqpaZ1lxY3RqlQcBU5uGsSoZj/hbF3ZweDqPbFHDkczT4emAxeaw37OD96RAAoayFGFQZCdWg==" }, "react-test-renderer": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.1.tgz", - "integrity": "sha512-Bd21TN3+YVl6GZwav6O0T6m5UwGfOj+2+xZH5VH93ToD6M5uclN/c+R1DGX49ueG413KZPUx7Kw3sOYz2aJgfg==", + "version": "16.8.4", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.4.tgz", + "integrity": "sha512-jQ9Tf/ilIGSr55Cz23AZ/7H3ABEdo9oy2zF9nDHZyhLHDSLKuoILxw2ifpBfuuwQvj4LCoqdru9iZf7gwFH28A==", "dev": true, "requires": { "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "react-is": "^16.8.1", - "scheduler": "^0.13.1" + "react-is": "^16.8.4", + "scheduler": "^0.13.4" }, "dependencies": { "react-is": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.1.tgz", - "integrity": "sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA==", + "version": "16.8.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.4.tgz", + "integrity": "sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==", "dev": true } } @@ -24689,9 +24741,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "scheduler": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.1.tgz", - "integrity": "sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A==", + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.4.tgz", + "integrity": "sha512-cvSOlRPxOHs5dAhP9yiS/6IDmVAVxmk33f0CtTJRkmUWcb1Us+t7b1wqdzoC0REw2muC9V5f1L/w5R5uKGaepA==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" diff --git a/package.json b/package.json index 2f6318db0..d99f36932 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "express-static-gzip": "^0.3.2", "farce": "^0.2.6", "fluent": "^0.10.0", - "found": "^0.3.14", + "found": "^0.3.21", "found-relay": "^0.3.1", "fs-extra": "^6.0.1", "graphql": "^0.13.2", @@ -170,7 +170,7 @@ "@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": "^16.8.7", "@types/react-copy-to-clipboard": "^4.2.5", "@types/react-dom": "^16.0.11", "@types/react-relay": "^1.3.9", @@ -263,15 +263,15 @@ "pstree.remy": "^1.1.0", "pym.js": "^1.3.2", "raw-loader": "^0.5.1", - "react": "^16.8.0", + "react": "^16.8.4", "react-copy-to-clipboard": "^5.0.1", "react-dev-utils": "6.0.0-next.3e165448", - "react-dom": "^16.8.0", + "react-dom": "^16.8.4", "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.8.0", + "react-test-renderer": "^16.8.4", "react-timeago": "^4.1.9", "react-transition-group": "^2.5.0", "react-with-state-props": "^2.0.4", diff --git a/src/core/client/admin/routes/configure/components/Configure.spec.tsx b/src/core/client/admin/routes/configure/components/Configure.spec.tsx index 117d7ff5f..8298085bb 100644 --- a/src/core/client/admin/routes/configure/components/Configure.spec.tsx +++ b/src/core/client/admin/routes/configure/components/Configure.spec.tsx @@ -8,8 +8,9 @@ import Configure from "./Configure"; it("renders correctly", () => { const props: PropTypesOf = { - onSave: noop, + onSubmit: noop, onChange: noop, + children: , }; const renderer = createRenderer(); renderer.render(); diff --git a/src/core/client/admin/routes/configure/components/Configure.tsx b/src/core/client/admin/routes/configure/components/Configure.tsx index 369db3340..f6d3e1960 100644 --- a/src/core/client/admin/routes/configure/components/Configure.tsx +++ b/src/core/client/admin/routes/configure/components/Configure.tsx @@ -12,17 +12,18 @@ import { Link, Navigation } from "./Navigation"; import SideBar from "./SideBar"; interface Props { - onSave: (settings: any, form: FormApi) => void; + onSubmit: (settings: any, form: FormApi) => void; onChange: (formState: FormState) => void; + children: React.ReactElement; } const Configure: StatelessComponent = ({ - onSave, + onSubmit, onChange, children, }) => ( -
+ {({ handleSubmit, submitting, pristine, form, submitError }) => ( diff --git a/src/core/client/admin/routes/configure/containers/ConfigureContainer.tsx b/src/core/client/admin/routes/configure/containers/ConfigureContainer.tsx index 2d1490e5c..cab17df10 100644 --- a/src/core/client/admin/routes/configure/containers/ConfigureContainer.tsx +++ b/src/core/client/admin/routes/configure/containers/ConfigureContainer.tsx @@ -1,5 +1,4 @@ import { FormApi, FormState } from "final-form"; -import { Router } from "found"; import React from "react"; import { @@ -7,117 +6,54 @@ import { UpdateSettingsMutation, withUpdateSettingsMutation, } from "talk-admin/mutations"; -import { TalkContext, withContext } from "talk-framework/lib/bootstrap"; -import { InvalidRequestError } from "talk-framework/lib/errors"; -import { getMessage } from "talk-framework/lib/i18n"; +import { SubmitHookHandler } from "talk-framework/lib/form"; import Configure from "../components/Configure"; -import { - AddSubmitHook, - SubmitHook, - SubmitHookContextProvider, -} from "../submitHook"; +import NavigationWarningContainer from "./NavigationWarningContainer"; interface Props { - localeBundles: TalkContext["localeBundles"]; - router: Router; updateSettings: UpdateSettingsMutation; - children: React.ReactNode; + children: React.ReactElement; } -class ConfigureContainer extends React.Component { - private dirty = false; - private removeTransitionHook: () => void; - private submitHooks: SubmitHook[] = []; +interface State { + dirty: boolean; +} - constructor(props: Props) { - super(props); +class ConfigureContainer extends React.Component { + public state: State = { + dirty: false, + }; - this.dirty = false; - - const warningMessage = getMessage( - props.localeBundles, - "configure-unsavedInputWarning", - "You have unsaved input. Are you sure you want to leave this page?" - ); - - this.removeTransitionHook = props.router.addTransitionHook( - () => (this.dirty ? warningMessage : true) - ); - } - - public componentWillUnmount() { - this.removeTransitionHook(); - } - - private handleSave = async ( + private handleExecute = async ( data: UpdateSettingsInput["settings"], form: FormApi ) => { - let cancelled = false; - let formErrors: Record = {}; - const executeCallbacks: Array<() => Promise> = []; - const cancel = (errors: Record) => { - cancelled = true; - formErrors = { ...errors, ...formErrors }; - }; - const onExecute = (cb: () => Promise) => { - executeCallbacks.push(cb); - }; - try { - // Call submit hooks, that can manipulate what - // we send as the mutation. - let nextData = data; - for (const hook of this.submitHooks) { - const result = await hook(nextData, { cancel, onExecute }); - if (result) { - nextData = result; - } - } - if (cancelled) { - return formErrors; - } - - executeCallbacks.push(() => - this.props.updateSettings({ settings: nextData }) - ); - for (const c of executeCallbacks.map(cb => cb())) { - await c; - } - form.initialize(data); - } catch (error) { - if (error instanceof InvalidRequestError) { - return error.invalidArgs; - } - // tslint:disable-next-line:no-console - console.error(error); - } - return undefined; + await this.props.updateSettings({ settings: data }); + form.initialize(data); }; private handleChange = ({ dirty }: FormState) => { - this.dirty = dirty; - }; - - private addSubmitHook: AddSubmitHook = hook => { - this.submitHooks.push(hook); - return () => { - this.submitHooks = this.submitHooks.filter(h => h !== hook); - }; + if (dirty !== this.state.dirty) { + this.setState({ dirty }); + } }; public render() { return ( - - - {this.props.children} - - + <> + + + {({ onSubmit }) => ( + + {this.props.children} + + )} + + ); } } -const enhanced = withContext(({ localeBundles }) => ({ localeBundles }))( - withUpdateSettingsMutation(ConfigureContainer) -); +const enhanced = withUpdateSettingsMutation(ConfigureContainer); export default enhanced; diff --git a/src/core/client/admin/routes/configure/containers/NavigationWarningContainer.tsx b/src/core/client/admin/routes/configure/containers/NavigationWarningContainer.tsx new file mode 100644 index 000000000..27c173781 --- /dev/null +++ b/src/core/client/admin/routes/configure/containers/NavigationWarningContainer.tsx @@ -0,0 +1,43 @@ +import { Match, Router, withRouter } from "found"; +import React from "react"; + +import { TalkContext, withContext } from "talk-framework/lib/bootstrap"; +import { getMessage } from "talk-framework/lib/i18n"; + +interface Props { + localeBundles: TalkContext["localeBundles"]; + router: Router; + active: boolean; + match: Match; +} + +class NavigationWarningContainer extends React.Component { + private removeTransitionHook: () => void; + + constructor(props: Props) { + super(props); + + const warningMessage = getMessage( + props.localeBundles, + "configure-unsavedInputWarning", + "You have unsaved input. Are you sure you want to leave this page?" + ); + + this.removeTransitionHook = props.router.addTransitionHook( + () => (this.props.active ? warningMessage : true) + ); + } + + public componentWillUnmount() { + this.removeTransitionHook(); + } + + public render() { + return null; + } +} + +const enhanced = withContext(({ localeBundles }) => ({ localeBundles }))( + withRouter(NavigationWarningContainer) +); +export default enhanced; diff --git a/src/core/client/admin/routes/configure/sections/auth/components/ConfigBoxWithToggleField.tsx b/src/core/client/admin/routes/configure/sections/auth/components/ConfigBoxWithToggleField.tsx index 50c09c4b2..cc27ec9e5 100644 --- a/src/core/client/admin/routes/configure/sections/auth/components/ConfigBoxWithToggleField.tsx +++ b/src/core/client/admin/routes/configure/sections/auth/components/ConfigBoxWithToggleField.tsx @@ -1,6 +1,7 @@ import { Localized } from "fluent-react/compat"; import React, { StatelessComponent } from "react"; import { Field } from "react-final-form"; +import { parseBool } from "talk-framework/lib/form"; import { CheckBox, FormField } from "talk-ui/components"; @@ -14,8 +15,6 @@ interface Props { children: (disabledInside: boolean) => React.ReactNode; } -const bool = (v: any) => !!v; - const ConfigBoxWithToggleField: StatelessComponent = ({ id, name, @@ -24,7 +23,7 @@ const ConfigBoxWithToggleField: StatelessComponent = ({ children, ...rest }) => ( - + {({ input }) => ( !!v; - interface Props { label: React.ReactNode; name: string; @@ -20,7 +19,7 @@ const TargetFilterField: StatelessComponent = ({ {label} - + {({ input, meta }) => ( = ({ )} - + {({ input }) => ( = ({ disabled }) => ( Write a message to appear after a story is closed for commenting. - + {({ input, meta }) => ( <> }> 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 index 7634ab452..be4b2b757 100644 --- a/src/core/client/admin/routes/configure/sections/general/components/ClosingCommentStreamsConfig.tsx +++ b/src/core/client/admin/routes/configure/sections/general/components/ClosingCommentStreamsConfig.tsx @@ -45,7 +45,7 @@ const ClosingCommentStreamsConfig: StatelessComponent = ({ Close Comments Automatically - + }> @@ -53,7 +53,7 @@ const ClosingCommentStreamsConfig: StatelessComponent = ({ { const enhanced = withFragmentContainer({ settings: graphql` fragment ClosedStreamMessageConfigContainer_settings on Settings { - closedMessage + closeCommenting { + message + } } `, })(ClosedStreamMessageConfigContainer); 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 index 31ba1d440..dd0505313 100644 --- a/src/core/client/admin/routes/configure/sections/general/containers/ClosingCommentStreamsConfigContainer.tsx +++ b/src/core/client/admin/routes/configure/sections/general/containers/ClosingCommentStreamsConfigContainer.tsx @@ -27,8 +27,10 @@ class ClosingCommentStreamsConfigContainer extends React.Component { const enhanced = withFragmentContainer({ settings: graphql` fragment ClosingCommentStreamsConfigContainer_settings on Settings { - autoCloseStream - closedTimeout + closeCommenting { + auto + timeout + } } `, })(ClosingCommentStreamsConfigContainer); 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 index 450a89ddc..a42f4a543 100644 --- a/src/core/client/admin/routes/configure/sections/organization/components/OrganizationContactEmailConfig.tsx +++ b/src/core/client/admin/routes/configure/sections/organization/components/OrganizationContactEmailConfig.tsx @@ -23,7 +23,7 @@ const OrganizationNameConfig: StatelessComponent = ({ disabled }) => (
+