diff --git a/.eslintrc.js b/.eslintrc.js index 0b2059c41..85f0cfd18 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -92,6 +92,8 @@ const typescriptOverrides = { "react/prop-types": "off", "react/no-unescaped-entities": "off", "no-empty-function": "off", + // (tessalt) disabled because video elements are only used to display gifs, which have no audio + "jsx-a11y/media-has-caption": "off", } ), }; diff --git a/package-lock.json b/package-lock.json index 3d2ba7617..52c5d5cdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5087,9 +5087,9 @@ } }, "@coralproject/rte": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@coralproject/rte/-/rte-1.0.0.tgz", - "integrity": "sha512-Z+NMytOSdz2qqphcevI9+s3CUzsW/LPzXPxVHAd9qKSlgJ+lr4pvht/gzgcvDvKy1hT88CompJwkqlIBxk0y9w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@coralproject/rte/-/rte-1.1.1.tgz", + "integrity": "sha512-r70xg7arHttiJr4pXCZTTWA86bjT9M+XHDeaYjNpMQrW5rIL+kw7sjoRuiyPLzi88xINMbxffurdKep2Z68bcg==", "dev": true, "requires": { "classnames": "^2.2.6", @@ -22068,6 +22068,13 @@ "concat-map": "0.0.1" } }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true, + "optional": true + }, "code-point-at": { "version": "1.1.0", "resolved": false, @@ -22127,12 +22134,28 @@ "dev": true, "optional": true }, - "fs.realpath": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "dev": true, - "optional": true + "optional": true, + "requires": { + "minipass": "^2.6.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } }, "gauge": { "version": "2.7.4", @@ -22245,14 +22268,42 @@ "brace-expansion": "^1.1.7" } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "optional": true, "requires": { - "minimist": "^1.2.5" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" } }, "ms": { @@ -22534,6 +22585,22 @@ "dev": true, "optional": true }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": false, @@ -22557,6 +22624,13 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true, + "optional": true } } }, @@ -29139,7 +29213,6 @@ "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "requires": { "chownr": "^1.0.1", - "minizlib": "^1.1.0", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.1", "yallist": "^3.0.2" @@ -38530,35 +38603,6 @@ "minipass": "^3.0.0" } }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -49223,9 +49267,9 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "squire-rte": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/squire-rte/-/squire-rte-1.10.1.tgz", - "integrity": "sha512-BGTSZmwY3BFFvLaGSz6qFwzdCbRc0EhTW9rw3xNwlEIVHKBFzImDdzT1aM4dWZYyvNvAHknUuPvr/elNU7Nt1g==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/squire-rte/-/squire-rte-1.10.2.tgz", + "integrity": "sha512-/IUZbc3+pdZUL+kP6wsUI8brDKtiP+EiIlw6Hj6q1n384NzUqQNa8iMLkeYZKC4o96ToyteY7fhv/DeS1GkWRw==", "dev": true }, "sshpk": { @@ -50805,59 +50849,6 @@ "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", "dev": true }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "optional": true - } - } - }, "tar-stream": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz", diff --git a/package.json b/package.json index 2c7a403c1..fa579a0f6 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "@babel/preset-typescript": "^7.10.1", "@babel/runtime-corejs3": "^7.10.3", "@coralproject/npm-run-all": "^4.1.5", - "@coralproject/rte": "^1.0.0", + "@coralproject/rte": "^1.1.1", "@fluent/react": "^0.11.1", "@intervolga/optimize-cssnano-plugin": "^1.0.6", "@types/archiver": "^3.1.0", diff --git a/src/core/client/admin/components/ModerateCard/GiphyMedia.tsx b/src/core/client/admin/components/ModerateCard/GiphyMedia.tsx new file mode 100644 index 000000000..6bca430b9 --- /dev/null +++ b/src/core/client/admin/components/ModerateCard/GiphyMedia.tsx @@ -0,0 +1,63 @@ +import { Localized } from "@fluent/react/compat"; +import React, { FunctionComponent, useCallback, useState } from "react"; + +import { BaseButton, Flex, Icon } from "coral-ui/components/v2"; + +import styles from "./Media.css"; + +interface Props { + still: string | null; + title: string | null; + width: number | null; + height: number | null; + video: string | null; +} + +const GiphyMedia: FunctionComponent = ({ + still, + title, + width, + height, + video, +}) => { + const [showAnimated, setShowAnimated] = useState(false); + const toggleImage = useCallback(() => { + setShowAnimated(!showAnimated); + }, [showAnimated]); + return ( +
+ {!showAnimated && still && ( + + {title + + + play_circle_outline + + +

Play GIF

+
+
+
+ )} + {showAnimated && video && ( + + + + )} +
+ ); +}; + +export default GiphyMedia; diff --git a/src/core/client/admin/components/ModerateCard/Media.css b/src/core/client/admin/components/ModerateCard/Media.css new file mode 100644 index 000000000..20c2e5644 --- /dev/null +++ b/src/core/client/admin/components/ModerateCard/Media.css @@ -0,0 +1,33 @@ +.embed { + margin: var(--v2-spacing-2) 0; +} + +.toggle { + position: relative; +} + +.toggleTrigger { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + background: rgba(101, 105, 107, 0.6); +} + +.playIcon { + color: var(--v2-colors-pure-white); +} + +.playText { + color: var(--v2-colors-pure-white); + font-size: var(--v2-font-size-2); + font-weight: var(--v2-font-weight-primary-bold); + font-family: var(--v2-font-family-primary); + text-transform: uppercase; + margin: 0; +} + +.image { + display: block; +} diff --git a/src/core/client/admin/components/ModerateCard/MediaContainer.tsx b/src/core/client/admin/components/ModerateCard/MediaContainer.tsx new file mode 100644 index 000000000..974863a70 --- /dev/null +++ b/src/core/client/admin/components/ModerateCard/MediaContainer.tsx @@ -0,0 +1,87 @@ +import React, { FunctionComponent } from "react"; +import { graphql } from "react-relay"; + +import { withFragmentContainer } from "coral-framework/lib/relay"; +import {} from "coral-ui/components/v2"; + +import { MediaContainer_comment } from "coral-admin/__generated__/MediaContainer_comment.graphql"; + +import GiphyMedia from "./GiphyMedia"; +import TwitterMedia from "./TwitterMedia"; +import YouTubeMedia from "./YouTubeMedia"; + +interface Props { + comment: MediaContainer_comment; +} + +const MediaContainer: FunctionComponent = ({ comment }) => { + if (!comment || !comment.revision || !comment.revision.media) { + return null; + } + return ( + <> + {comment.revision.media.__typename === "GiphyMedia" && ( + + )} + {comment.revision.media.__typename === "TwitterMedia" && ( + + )} + {comment.revision.media.__typename === "YouTubeMedia" && ( + + )} + + ); +}; + +const enhanced = withFragmentContainer({ + comment: graphql` + fragment MediaContainer_comment on Comment { + site { + id + } + revision { + media { + __typename + ... on GiphyMedia { + url + title + width + height + still + video + } + ... on TwitterMedia { + url + width + } + ... on YouTubeMedia { + url + still + title + width + height + } + } + } + } + `, +})(MediaContainer); + +export default enhanced; diff --git a/src/core/client/admin/components/ModerateCard/ModerateCard.tsx b/src/core/client/admin/components/ModerateCard/ModerateCard.tsx index 7a54986a0..cb3500241 100644 --- a/src/core/client/admin/components/ModerateCard/ModerateCard.tsx +++ b/src/core/client/admin/components/ModerateCard/ModerateCard.tsx @@ -29,6 +29,7 @@ import ApproveButton from "./ApproveButton"; import CommentAuthorContainer from "./CommentAuthorContainer"; import FeatureButton from "./FeatureButton"; import MarkersContainer from "./MarkersContainer"; +import MediaContainer from "./MediaContainer"; import RejectButton from "./RejectButton"; import styles from "./ModerateCard.css"; @@ -44,7 +45,8 @@ interface Props { username: string | null; } | null; comment: PropTypesOf["comment"] & - PropTypesOf["comment"]; + PropTypesOf["comment"] & + PropTypesOf["comment"]; settings: PropTypesOf["settings"]; status: "approved" | "rejected" | "undecided"; featured: boolean; @@ -238,13 +240,12 @@ const ModerateCard: FunctionComponent = ({ )}
- - {commentBody} - +
+ + {commentBody} + + +
{onConversationClick && (