X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/c324b64f3b26e79b4640b6f0cf55671f1a261bca..e2ac5434eb436b0e8ca8205dfe83d4ef1bcdd4c5:/src/components/code-snippet/code-snippet.tsx

diff --git a/src/components/code-snippet/code-snippet.tsx b/src/components/code-snippet/code-snippet.tsx
index 6cba299f..83c378b8 100644
--- a/src/components/code-snippet/code-snippet.tsx
+++ b/src/components/code-snippet/code-snippet.tsx
@@ -2,37 +2,84 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import * as React from 'react';
-import { StyleRulesCallback, WithStyles, Typography, withStyles, Theme } from '@material-ui/core';
-import { ArvadosTheme } from '~/common/custom-theme';
-import * as classNames from 'classnames';
+import React from 'react';
+import { StyleRulesCallback, WithStyles, Typography, withStyles, Link } from '@material-ui/core';
+import { ArvadosTheme } from 'common/custom-theme';
+import classNames from 'classnames';
+import { connect, DispatchProp } from 'react-redux';
+import { RootState } from 'store/store';
+import { FederationConfig, getNavUrl } from 'routes/routes';
+import { Dispatch } from 'redux';
+import { navigationNotAvailable } from 'store/navigation/navigation-action';
 
-type CssRules = 'root';
+type CssRules = 'root' | 'space';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     root: {
         boxSizing: 'border-box',
         overflow: 'auto',
         padding: theme.spacing.unit
+    },
+    space: {
+        marginLeft: '15px'
     }
 });
 
 export interface CodeSnippetDataProps {
     lines: string[];
     className?: string;
+    apiResponse?: boolean;
+    linked?: boolean;
+}
+
+interface CodeSnippetAuthProps {
+    auth: FederationConfig;
 }
 
 type CodeSnippetProps = CodeSnippetDataProps & WithStyles<CssRules>;
 
-export const CodeSnippet = withStyles(styles)(
-    ({ classes, lines, className }: CodeSnippetProps) =>
-        <Typography 
-        component="div" 
+const mapStateToProps = (state: RootState): CodeSnippetAuthProps => ({
+    auth: state.auth,
+});
+
+export const CodeSnippet = withStyles(styles)(connect(mapStateToProps)(
+    ({ classes, lines, linked, className, apiResponse, dispatch, auth }: CodeSnippetProps & CodeSnippetAuthProps & DispatchProp) =>
+        <Typography
+        component="div"
         className={classNames(classes.root, className)}>
-            {
-                lines.map((line: string, index: number) => {
-                    return <Typography key={index} component="pre">{line}</Typography>;
-                })
-            }
+            <Typography className={apiResponse ? classes.space : className} component="pre">
+                {linked ?
+                    lines.map((line, index) => <React.Fragment key={index}>{renderLinks(auth, dispatch)(line)}{`\n`}</React.Fragment>) :
+                    lines.join('\n')
+                }
+            </Typography>
         </Typography>
-    );
\ No newline at end of file
+    ));
+
+const renderLinks = (auth: FederationConfig, dispatch: Dispatch) => (text: string): JSX.Element => {
+    // Matches UUIDs & PDHs
+    const REGEX = /[a-z0-9]{5}-[a-z0-9]{5}-[a-z0-9]{15}|[0-9a-f]{32}\+\d+/g;
+    const links = text.match(REGEX);
+    if (!links) {
+        return <>{text}</>;
+    }
+    return <>
+        {text.split(REGEX).map((part, index) =>
+            <React.Fragment key={index}>
+                {part}
+                {links[index] &&
+                <Link onClick={() => {
+                    const url = getNavUrl(links[index], auth)
+                    if (url) {
+                        window.open(`${window.location.origin}${url}`, '_blank');
+                    } else {
+                        dispatch(navigationNotAvailable(links[index]));
+                    }
+                }}
+                    style={ {cursor: 'pointer'} }>
+                    {links[index]}
+                </Link>}
+            </React.Fragment>
+        )}
+    </>;
+  };
