Skip to content

Commit

Permalink
show confirm modal when open link in message (#1684)
Browse files Browse the repository at this point in the history
  • Loading branch information
notmd committed Feb 17, 2023
1 parent faef499 commit a051357
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 26 deletions.
1 change: 1 addition & 0 deletions website/public/locales/en/common.json
Expand Up @@ -4,6 +4,7 @@
"admin_dashboard": "Admin Dashboard",
"back_to_dashboard": "Go back to the dashboard",
"cancel": "Cancel",
"confirm": "Confirm",
"connect": "Connect",
"conversational": "Conversational AI for everyone.",
"copied": "Copied",
Expand Down
6 changes: 4 additions & 2 deletions website/public/locales/en/message.json
@@ -1,13 +1,15 @@
{
"confirm_open_link_body": "Do you want to open this link?",
"confirm_open_link_header": "Confirm open link",
"copy_message_id": "Copy message ID",
"copy_message_link": "Copy message link",
"label_action": "Label",
"label_title": "Label",
"message_author": "Author",
"message_author_explain": "You are the author of this message",
"message_author": "Author",
"message_deleted": "Message deleted",
"no_messages": "No messages",
"message": "Message",
"no_messages": "No messages",
"open_new_tab_action": "Open in new tab",
"parent": "Parent",
"reactions": "Reactions",
Expand Down
1 change: 1 addition & 0 deletions website/src/components/Messages/MessageTableEntry.tsx
Expand Up @@ -124,6 +124,7 @@ export function MessageTableEntry({ message, enabled, highlight, showAuthorBadge
onClick={goToMessage}
cursor={enabled ? "pointer" : undefined}
style={{ position: "relative" }}
overflow="hidden"
>
{inlineAvatar && avatar}
<Suspense fallback={message.text}>
Expand Down
126 changes: 102 additions & 24 deletions website/src/components/Messages/RenderedMarkdown.tsx
@@ -1,31 +1,29 @@
import { SystemStyleObject } from "@chakra-ui/react";
import {
Box,
Button,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
SystemStyleObject,
useDisclosure,
} from "@chakra-ui/react";
import { Prose } from "@nikolovlazar/chakra-ui-prose";
import { memo } from "react";
import { ReactMarkdown } from "react-markdown/lib/react-markdown";
import NextLink from "next/link";
import { useTranslation } from "next-i18next";
import { memo, MouseEvent, useMemo, useState } from "react";
import ReactMarkdown from "react-markdown";
import type { ReactMarkdownOptions } from "react-markdown/lib/react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import remarkGfm from "remark-gfm";
interface RenderedMarkdownProps {
markdown: string;
}

const components = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
code({ node, inline, className, children, style, ...props }) {
const match = /language-(\w+)/.exec(className || "");
const lang = match ? match[1] : "";
return !inline ? (
<SyntaxHighlighter style={oneDark} language={lang} {...props}>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
};

const sx: SystemStyleObject = {
overflowX: "auto",
pre: {
Expand Down Expand Up @@ -74,12 +72,92 @@ const sx: SystemStyleObject = {
const plugins = [remarkGfm];

// eslint-disable-next-line react/display-name
const RenderedMarkdown = memo(({ markdown }: RenderedMarkdownProps) => {
const RenderedMarkdown = ({ markdown }: RenderedMarkdownProps) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const [link, setLink] = useState<string | undefined>();

const components: ReactMarkdownOptions["components"] = useMemo(() => {
return {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
code({ node, inline, className, children, style, ...props }) {
const match = /language-(\w+)/.exec(className || "");
const lang = match ? match[1] : "";
return !inline ? (
<SyntaxHighlighter style={oneDark} language={lang} {...props}>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
a({ href, ...props }) {
if (!href) {
return props.children;
}

return (
<NextLink // use NextLink to handle locale if link is our internal, but it's really edge case.
{...props}
href={href}
target="_blank"
rel="noopener noreferrer"
onClick={(e: MouseEvent) => {
e.preventDefault();
setLink(href);
onOpen();
}}
></NextLink>
);
},
} as ReactMarkdownOptions["components"];
}, [onOpen]);

const linkProps = useMemo(() => {
return {
as: NextLink,
href: link,
target: "_blank",
rel: "noopener noreferrer",
};
}, [link]);

const { t } = useTranslation(["common", "message"]);

return (
<>
<MemorizedMarkdown components={components}>{markdown}</MemorizedMarkdown>
<Modal isOpen={isOpen} onClose={onClose} isCentered>
<ModalOverlay />
<ModalContent>
<ModalHeader>{t("message:confirm_open_link_header")}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<div>{t("message:confirm_open_link_body")}</div>
<Box textDecoration="underline" {...linkProps}>
{link}
</Box>
</ModalBody>
<ModalFooter>
<Button variant="ghost" mr={3} onClick={onClose}>
{t("cancel")}
</Button>
<Button colorScheme="blue" as={NextLink} {...linkProps} onClick={onClose}>
{t("confirm")}
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};

// eslint-disable-next-line react/display-name
const MemorizedMarkdown = memo((props: ReactMarkdownOptions) => {
return (
<Prose as="div" sx={sx}>
<ReactMarkdown remarkPlugins={plugins} components={components}>
{markdown}
</ReactMarkdown>
<ReactMarkdown remarkPlugins={plugins} {...props}></ReactMarkdown>
</Prose>
);
});
Expand Down

0 comments on commit a051357

Please sign in to comment.