diff --git a/webui/package-lock.json b/webui/package-lock.json index 12dea8b969fcabba9ff1466ce1876034f044dd47..b3b2a4907213cc85fa161b95e7ebf5606d1da1b3 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -12325,6 +12325,11 @@ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, + "gemoji": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gemoji/-/gemoji-6.1.0.tgz", + "integrity": "sha512-MOlX3doQ1fsfzxQX8Y+u6bC5Ssc1pBUBIPVyrS69EzKt+5LIZAOm0G5XGVNhwXFgkBF3r+Yk88ONyrFHo8iNFA==" + }, "generic-names": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz", @@ -12572,7 +12577,8 @@ "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "optional": true }, "gzip-size": { "version": "5.1.1", @@ -16118,6 +16124,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -16131,6 +16138,7 @@ "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "optional": true, "requires": { "lru-cache": "^6.0.0" } @@ -16138,12 +16146,14 @@ "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -19227,6 +19237,15 @@ "fbjs": "^1.0.0" } }, + "remark-gemoji": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-gemoji/-/remark-gemoji-6.0.0.tgz", + "integrity": "sha512-LDW2h6QqNzAbAcOjscgfkJW9/8TGBasBe/ji+3mCxHlJdhF2IEXFSmm/3tdEPP1JJDZ4y+Ea+xlFQ4tOIU9WvA==", + "requires": { + "gemoji": "^6.0.0", + "unist-util-visit": "^2.0.0" + } + }, "remark-html": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-12.0.0.tgz", @@ -20219,7 +20238,8 @@ "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "optional": true }, "side-channel": { "version": "1.0.3", diff --git a/webui/package.json b/webui/package.json index 39696a25918f7fe4b2bd1f8748f1e83097ab9b69..47cdf8d02c56207ea6d87584d9f95f9d7f223156 100644 --- a/webui/package.json +++ b/webui/package.json @@ -22,6 +22,7 @@ "react-router": "^5.2.0", "react-router-dom": "^5.2.0", "react-scripts": "^4.0.0-next.98", + "remark-gemoji": "^6.0.0", "remark-html": "^12.0.0", "remark-parse": "^8.0.3", "remark-react": "^7.0.1", diff --git a/webui/src/components/CommentInput/CommentInput.tsx b/webui/src/components/CommentInput/CommentInput.tsx index c574538e886f220675bf839dc91c6ef7ac25c4b8..f12ee8d8f1e35cd88a02c7d49891d1affa15dcd8 100644 --- a/webui/src/components/CommentInput/CommentInput.tsx +++ b/webui/src/components/CommentInput/CommentInput.tsx @@ -5,7 +5,7 @@ import Tabs from '@material-ui/core/Tabs'; import TextField from '@material-ui/core/TextField'; import { makeStyles } from '@material-ui/core/styles'; -import Content from 'src/components/Content'; +import Content from '../Content'; /** * Styles diff --git a/webui/src/components/Content/AnchorTag.tsx b/webui/src/components/Content/AnchorTag.tsx new file mode 100644 index 0000000000000000000000000000000000000000..47d5e2fac3433951f3f10cfef9012f92239efd91 --- /dev/null +++ b/webui/src/components/Content/AnchorTag.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme) => ({ + tag: { + color: theme.palette.text.secondary, + }, +})); + +const AnchorTag = ({ children, href }: React.HTMLProps) => { + const classes = useStyles(); + const origin = window.location.origin; + const destination = href === undefined ? '' : href; + const isInternalLink = + destination.startsWith('/') || destination.startsWith(origin); + const internalDestination = destination.replace(origin, ''); + const internalLink = ( + + {children} + + ); + const externalLink = ( + + {children} + + ); + + return isInternalLink ? internalLink : externalLink; +}; + +export default AnchorTag; diff --git a/webui/src/components/Content/BlockQuoteTag.tsx b/webui/src/components/Content/BlockQuoteTag.tsx new file mode 100644 index 0000000000000000000000000000000000000000..18c34d8a9c5d1f7e5eba75424833a28612a3ccf3 --- /dev/null +++ b/webui/src/components/Content/BlockQuoteTag.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme) => ({ + tag: { + color: theme.palette.text.secondary, + borderLeftWidth: '0.5ch', + borderLeftStyle: 'solid', + borderLeftColor: theme.palette.text.secondary, + marginLeft: 0, + paddingLeft: '0.5rem', + }, +})); + +const BlockQuoteTag = (props: React.HTMLProps) => { + const classes = useStyles(); + return
; +}; + +export default BlockQuoteTag; diff --git a/webui/src/components/Content/ImageTag.tsx b/webui/src/components/Content/ImageTag.tsx index 70ee1bc03eca2182b87e9fdd186f7f80a7b418bf..29b01da305a5f693d1129720f2db83aad4319cd2 100644 --- a/webui/src/components/Content/ImageTag.tsx +++ b/webui/src/components/Content/ImageTag.tsx @@ -14,9 +14,12 @@ const ImageTag = ({ }: React.ImgHTMLAttributes) => { const classes = useStyles(); return ( - - {alt} - + <> + + {alt} + +
+ ); }; diff --git a/webui/src/components/Content/index.tsx b/webui/src/components/Content/index.tsx index 56e52e1efcfc530815e6cf80987ca3ddb47b82d2..e40188093c398842808ab02d7f18f2eb6e9be646 100644 --- a/webui/src/components/Content/index.tsx +++ b/webui/src/components/Content/index.tsx @@ -1,26 +1,32 @@ import React from 'react'; +import gemoji from 'remark-gemoji'; import html from 'remark-html'; import parse from 'remark-parse'; import remark2react from 'remark-react'; import unified from 'unified'; +import AnchorTag from './AnchorTag'; +import BlockQuoteTag from './BlockQuoteTag'; import ImageTag from './ImageTag'; import PreTag from './PreTag'; type Props = { markdown: string }; const Content: React.FC = ({ markdown }: Props) => { - const processor = unified() + const content = unified() .use(parse) + .use(gemoji) .use(html) .use(remark2react, { remarkReactComponents: { img: ImageTag, pre: PreTag, + a: AnchorTag, + blockquote: BlockQuoteTag, }, - }); + }) + .processSync(markdown).result; - const contents: React.ReactNode = processor.processSync(markdown).contents; - return <>{contents}; + return <>{content}; }; export default Content; diff --git a/webui/src/pages/bug/CommentForm.tsx b/webui/src/pages/bug/CommentForm.tsx index e70348a6e883f7ffcb6be3a331322d9f4698250c..a8ce431950fa6bbd002d4512010c61e27a8ee907 100644 --- a/webui/src/pages/bug/CommentForm.tsx +++ b/webui/src/pages/bug/CommentForm.tsx @@ -17,14 +17,6 @@ const useStyles = makeStyles((theme) => ({ container: { padding: theme.spacing(0, 2, 2, 2), }, - textarea: {}, - tabContent: { - margin: theme.spacing(2, 0), - }, - preview: { - borderBottom: `solid 3px ${theme.palette.grey['200']}`, - minHeight: '5rem', - }, actions: { display: 'flex', gap: '1em', diff --git a/webui/src/pages/bug/Message.tsx b/webui/src/pages/bug/Message.tsx index 2f4cbc592ee6f3016426eec371899c3efcd43b99..39b11ccdae7b3ab25dc9f50ccbda9ccef98321a9 100644 --- a/webui/src/pages/bug/Message.tsx +++ b/webui/src/pages/bug/Message.tsx @@ -57,7 +57,8 @@ const useStyles = makeStyles((theme) => ({ }, body: { ...theme.typography.body2, - padding: '0.5rem', + paddingLeft: theme.spacing(1), + paddingRight: theme.spacing(1), }, headerActions: { color: theme.palette.info.contrastText, diff --git a/webui/src/pages/bug/MessageHistoryDialog.tsx b/webui/src/pages/bug/MessageHistoryDialog.tsx index 0ed33642cec6366e438457e2a5184d687b6018e1..5879a373aa3e82a800580497b1d223819f001836 100644 --- a/webui/src/pages/bug/MessageHistoryDialog.tsx +++ b/webui/src/pages/bug/MessageHistoryDialog.tsx @@ -22,6 +22,8 @@ import { import CloseIcon from '@material-ui/icons/Close'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import Content from '../../components/Content'; + import { AddCommentFragment } from './MessageCommentFragment.generated'; import { CreateFragment } from './MessageCreateFragment.generated'; import { useMessageHistoryQuery } from './MessageHistory.generated'; @@ -108,6 +110,7 @@ const AccordionSummary = withStyles((theme) => ({ const AccordionDetails = withStyles((theme) => ({ root: { + display: 'block', padding: theme.spacing(2), }, }))(MuiAccordionDetails); @@ -214,6 +217,7 @@ function MessageHistoryDialog({ bugId, commentId, open, onClose }: Props) { {history?.map((edit, index) => ( @@ -224,7 +228,9 @@ function MessageHistoryDialog({ bugId, commentId, open, onClose }: Props) { > {getSummary(index, edit.date)} - {edit.message} + + + ))} diff --git a/webui/types/remark-gemoji/index.d.ts b/webui/types/remark-gemoji/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..f15725b2f8ae6c8013f1635cb2855be6785c81ad --- /dev/null +++ b/webui/types/remark-gemoji/index.d.ts @@ -0,0 +1,6 @@ +declare module 'remark-gemoji' { + import { Plugin } from 'unified'; + + const plugin: Plugin; + export default plugin; +}