diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 7a8d304..cbd3b49 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -2,8 +2,10 @@ import * as Icons from "@mdi/js"; import { Icon as MdiIcon } from "@mdi/react"; import { IconProps as IconBaseProps } from "@mdi/react/dist/IconProps"; +export type IconType = keyof typeof Icons; + export interface IconProps extends Omit { - icon: keyof typeof Icons; + icon: IconType; } function Icon(props: IconProps) { diff --git a/src/components/Spoiler.tsx b/src/components/Spoiler.tsx index 7ae548b..8dd8c0a 100644 --- a/src/components/Spoiler.tsx +++ b/src/components/Spoiler.tsx @@ -1,7 +1,7 @@ import React from "react"; import styled from "styled-components"; -const Container = styled.div` +const Container = styled.span` background-color: hsl(var(--background-tertiary-hsl)); padding: 0 5px; border-radius: 4px; diff --git a/src/components/markdown/MarkdownRenderer.tsx b/src/components/markdown/MarkdownRenderer.tsx index 3901160..8aba97c 100644 --- a/src/components/markdown/MarkdownRenderer.tsx +++ b/src/components/markdown/MarkdownRenderer.tsx @@ -1,6 +1,7 @@ import { FormattingPatterns } from "@spacebarchat/spacebar-api-types/v9"; import Marked, { ReactRenderer } from "marked-react"; import React from "react"; +import reactStringReplace from "react-string-replace"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { materialDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; import styled from "styled-components"; @@ -8,6 +9,7 @@ import CodeBlock from "../Codeblock"; import Link from "../Link"; import Spoiler from "../Spoiler"; import { MarkdownProps } from "./Markdown"; +import Mention from "./Mention"; import Timestamp from "./Timestamp"; const Container = styled.div` @@ -125,21 +127,39 @@ const customRenderer: Partial = { codespan: (content: string) => {content}, link: (href, text) => {text}, text: (text: string) => { - const spoilerRe = /\|\|([\s\S]+?)\|\|/g; + let replaced: string | React.ReactNode[] = text; + const SpoilerRegex = /\|\|(?[\s\S]+?)\|\|/; - if (spoilerRe.test(text)) { - // get content inside spoiler tags - const spoilerContent = text.match(spoilerRe)![0].slice(2, -2); - return ; - } + // replace the spoiler with the spoiler component + replaced = reactStringReplace(text, SpoilerRegex, (match, i) => { + return ; + }); - const match = text.match(FormattingPatterns.Timestamp); - if (match) { - const { timestamp, style } = match.groups || {}; - return ; - } + replaced = reactStringReplace(replaced, FormattingPatterns.DefaultStyledTimestamp, (match, i) => ( + + )); - return text; + replaced = reactStringReplace(replaced, //, (match, i) => { + const parts = match.split(":"); + const timestamp = parts[0]; + const style = parts[1]; + + return ; + }); + + replaced = reactStringReplace(replaced, FormattingPatterns.Channel, (match, i) => ( + + )); + + replaced = reactStringReplace(replaced, FormattingPatterns.User, (match, i) => ( + + )); + + replaced = reactStringReplace(replaced, FormattingPatterns.Role, (match, i) => ( + + )); + + return replaced; }, }; diff --git a/src/components/markdown/Mention.tsx b/src/components/markdown/Mention.tsx new file mode 100644 index 0000000..335ce8a --- /dev/null +++ b/src/components/markdown/Mention.tsx @@ -0,0 +1,71 @@ +import React, { memo } from "react"; +import styled from "styled-components"; +import { useAppStore } from "../../stores/AppStore"; +import User from "../../stores/objects/User"; + +const Container = styled.span` + padding: 0 2px; + border-radius: 4px; + background-color: hsl(var(--primary-hsl) / 0.3); + + &:hover { + background-color: hsl(var(--primary-hsl) / 0.5); + cursor: pointer; + } +`; + +interface MentionProps { + id: string; +} +function UserMention({ id }: MentionProps) { + const app = useAppStore(); + const [user, setUser] = React.useState(null); + + React.useEffect(() => { + const user = app.users.get(id); + if (user) setUser(user); + }, [id]); + + if (!user) + return ( + + @{id} + + ); + + return ( + + @{user.username} + + ); +} + +function ChannelMention({ id }: MentionProps) { + return ( + + #{id} + + ); +} + +function RoleMention({ id }: MentionProps) { + return ( + + @{id} + + ); +} + +interface Props { + type: "role" | "user" | "channel"; + id: string; +} + +function Mention({ type, id }: Props) { + if (type === "role") return ; + if (type === "user") return ; + if (type === "channel") return ; + return null; +} + +export default memo(Mention); diff --git a/src/components/markdown/Timestamp.tsx b/src/components/markdown/Timestamp.tsx index c60d01d..0686dd1 100644 --- a/src/components/markdown/Timestamp.tsx +++ b/src/components/markdown/Timestamp.tsx @@ -12,7 +12,7 @@ const Container = styled.div` interface Props { timestamp: string; - style: string; + style?: string; } function Timestamp({ timestamp, style }: Props) {