Skip to content

Commit

Permalink
feat(frontend): add inline markdown render for hints and notices
Browse files Browse the repository at this point in the history
  • Loading branch information
GZTimeWalker committed Aug 4, 2023
1 parent 3865856 commit 9f7545e
Show file tree
Hide file tree
Showing 41 changed files with 154 additions and 83 deletions.
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/AccountView.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FC } from 'react'
import { useNavigate } from 'react-router-dom'
import { Stack, Center, createStyles } from '@mantine/core'
import LogoHeader from './LogoHeader'
import LogoHeader from '@Components/LogoHeader'

const useStyles = createStyles(() => ({
input: {
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/AppFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { FC } from 'react'
import { Text, Stack, Divider, Anchor, Center } from '@mantine/core'
import FooterRender from '@Components/FooterRender'
import MainIcon from '@Components/icon/MainIcon'
import { useFooterStyles, useIsMobile, useLogoStyles } from '@Utils/ThemeOverride'
import { useConfig } from '@Utils/useConfig'
import FooterRender from './FooterRender'

const AppFooter: FC = () => {
const { config } = useConfig()
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/AppHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {
mdiCached,
} from '@mdi/js'
import { Icon } from '@mdi/react'
import LogoHeader from '@Components/LogoHeader'
import { useIsMobile } from '@Utils/ThemeOverride'
import { useLocalStorageCache } from '@Utils/useConfig'
import { useLoginOut, useUser } from '@Utils/useUser'
import LogoHeader from './LogoHeader'

const useHeaderStyles = createStyles((theme) => ({
header: {
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/AppNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import {
mdiCached,
} from '@mdi/js'
import { Icon } from '@mdi/react'
import MainIcon from '@Components/icon/MainIcon'
import { useLocalStorageCache } from '@Utils/useConfig'
import { useLoginOut, useUser } from '@Utils/useUser'
import { Role } from '@Api'
import MainIcon from './icon/MainIcon'

const useStyles = createStyles((theme) => {
const active = { ref: getStylesRef('activeItem') } as const
Expand Down
11 changes: 7 additions & 4 deletions src/GZCTF/ClientApp/src/components/ChallengeDetailModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import { useClipboard, useDisclosure, useInputState } from '@mantine/hooks'
import { notifications, showNotification, updateNotification } from '@mantine/notifications'
import { mdiCheck, mdiClose, mdiDownload, mdiLightbulbOnOutline, mdiLoading } from '@mdi/js'
import { Icon } from '@mdi/react'
import MarkdownRender, { InlineMarkdownRender } from '@Components/MarkdownRender'
import { showErrorNotification } from '@Utils/ApiErrorHandler'
import { ChallengeTagItemProps } from '@Utils/Shared'
import { useTooltipStyles } from '@Utils/ThemeOverride'
import { OnceSWRConfig } from '@Utils/useConfig'
import { useTypographyStyles } from '@Utils/useTypographyStyles'
import api, { AnswerResult, ChallengeType } from '@Api'
import MarkdownRender from './MarkdownRender'

interface ChallengeDetailModalProps extends ModalProps {
gameId: number
Expand Down Expand Up @@ -386,9 +386,12 @@ const ChallengeDetailModal: FC<ChallengeDetailModalProps> = (props) => {
{challenge.hints.map((hint) => (
<Group spacing="xs" align="flex-start" noWrap>
<Icon path={mdiLightbulbOnOutline} size={0.8} color={theme.colors.yellow[5]} />
<Text key={hint} size="sm" maw="calc(100% - 2rem)">
{hint}
</Text>
<InlineMarkdownRender
key={hint}
size="sm"
maw="calc(100% - 2rem)"
source={hint}
/>
</Group>
))}
</Stack>
Expand Down
8 changes: 4 additions & 4 deletions src/GZCTF/ClientApp/src/components/ChallengePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ import {
import { useLocalStorage } from '@mantine/hooks'
import { mdiFileUploadOutline, mdiFlagOutline, mdiPuzzle } from '@mdi/js'
import { Icon } from '@mdi/react'
import ChallengeCard from '@Components/ChallengeCard'
import ChallengeDetailModal from '@Components/ChallengeDetailModal'
import Empty from '@Components/Empty'
import WriteupSubmitModal from '@Components/WriteupSubmitModal'
import { ChallengeTagLabelMap, SubmissionTypeIconMap } from '@Utils/Shared'
import { useGame } from '@Utils/useGame'
import api, { ChallengeInfo, ChallengeTag, SubmissionType } from '@Api'
import ChallengeCard from './ChallengeCard'
import ChallengeDetailModal from './ChallengeDetailModal'
import Empty from './Empty'
import WriteupSubmitModal from './WriteupSubmitModal'

const DEFAULT_COLS = 8
const GRID_BREAKPOINTS = [
Expand Down
8 changes: 2 additions & 6 deletions src/GZCTF/ClientApp/src/components/FooterRender.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { marked } from 'marked'
import { forwardRef } from 'react'
import { Sx, TypographyStylesProvider, createStyles } from '@mantine/core'
import { TypographyStylesProvider, createStyles } from '@mantine/core'
import { MarkdownProps } from '@Components/MarkdownRender'
import { useIsMobile } from '@Utils/ThemeOverride'

interface MarkdownProps extends React.ComponentPropsWithoutRef<'div'> {
source: string
sx?: Sx | (Sx | undefined)[]
}

const useFooterStyles = createStyles((theme) => {
const sc = (dark: any, light: any) => (theme.colorScheme === 'dark' ? dark : light)
const cs = theme.colors
Expand Down
5 changes: 3 additions & 2 deletions src/GZCTF/ClientApp/src/components/GameNoticePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { showNotification } from '@mantine/notifications'
import { mdiClose } from '@mdi/js'
import { Icon } from '@mdi/react'
import * as signalR from '@microsoft/signalr'
import Empty from '@Components/Empty'
import { InlineMarkdownRender } from '@Components/MarkdownRender'
import { NoticTypeIconMap } from '@Utils/Shared'
import api, { GameNotice, NoticeType } from '@Api'
import Empty from './Empty'

enum NoticeFilter {
All = 'all',
Expand Down Expand Up @@ -162,7 +163,7 @@ const GameNoticePanel: FC = () => {
<Text size="xs" fw={700} c="dimmed">
{dayjs(notice.time).format('YY/MM/DD HH:mm:ss')}
</Text>
<Text>{notice.content}</Text>
<InlineMarkdownRender source={notice.content} />
</Stack>
</List.Item>
))}
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/IconTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
MantineColor,
} from '@mantine/core'
import { clamp } from '@mantine/hooks'
import LogoHeader from './LogoHeader'
import LogoHeader from '@Components/LogoHeader'

interface TabStyleProps {
color?: MantineColor
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/LogoHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { forwardRef } from 'react'
import { Group, Title, createStyles, GroupProps } from '@mantine/core'
import MainIcon from '@Components/icon/MainIcon'
import { useConfig } from '@Utils/useConfig'
import MainIcon from './icon/MainIcon'

const useStyles = createStyles((theme) => ({
brand: {
Expand Down
32 changes: 27 additions & 5 deletions src/GZCTF/ClientApp/src/components/MarkdownRender.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import 'katex/dist/katex.min.css'
import { marked } from 'marked'
import Prism from 'prismjs'
import { forwardRef } from 'react'
import { Sx, TypographyStylesProvider } from '@mantine/core'
import { useTypographyStyles } from '@Utils/useTypographyStyles'
import { Sx, Text, TextProps, TypographyStylesProvider } from '@mantine/core'
import { useInlineStyles, useTypographyStyles } from '@Utils/useTypographyStyles'

interface MarkdownProps extends React.ComponentPropsWithoutRef<'div'> {
export interface MarkdownProps extends React.ComponentPropsWithoutRef<'div'> {
source: string
sx?: Sx | (Sx | undefined)[]
}

interface InlineMarkdownProps extends TextProps {
source: string
}

const RenderReplacer = (func: any, replacer: (text: string) => string) => {
const original = func
return (...args: any[]) => {
Expand All @@ -19,18 +23,36 @@ const RenderReplacer = (func: any, replacer: (text: string) => string) => {
}
}

export const InlineMarkdownRender = forwardRef<HTMLParagraphElement, InlineMarkdownProps>(
(props, ref) => {
const { source, ...others } = props
const { classes, cx } = useInlineStyles()

return (
<Text
ref={ref}
className={others.className ? cx(classes.root, others.className) : classes.root}
{...others}
dangerouslySetInnerHTML={{
__html: marked.parseInline(source, { silent: true }) ?? '',
}}
/>
)
}
)

export const MarkdownRender = forwardRef<HTMLDivElement, MarkdownProps>((props, ref) => {
const { classes, cx } = useTypographyStyles()
const { source, ...others } = props

const renderer = new marked.Renderer()

const replacer = ((blockRegex, inlineRegex) => (text: string) => {
text = text.replace(blockRegex, (match, expression) => {
text = text.replace(blockRegex, (_, expression) => {
return katex.renderToString(expression, { displayMode: true, throwOnError: false })
})

text = text.replace(inlineRegex, (match, expression) => {
text = text.replace(inlineRegex, (_, expression) => {
return katex.renderToString(expression, { displayMode: false, throwOnError: false })
})

Expand Down
6 changes: 3 additions & 3 deletions src/GZCTF/ClientApp/src/components/MobilePostCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { useNavigate } from 'react-router-dom'
import { Group, Card, Title, Text, Stack, ActionIcon, Box, Avatar } from '@mantine/core'
import { mdiPencilOutline, mdiPinOffOutline, mdiPinOutline } from '@mdi/js'
import { Icon } from '@mdi/react'
import MarkdownRender from '@Components/MarkdownRender'
import { PostCardProps } from '@Components/PostCard'
import { RequireRole } from '@Components/WithRole'
import { useUserRole } from '@Utils/useUser'
import { Role } from '@Api'
import MarkdownRender from './MarkdownRender'
import { PostCardProps } from './PostCard'
import { RequireRole } from './WithRole'

const MobilePostCard: FC<PostCardProps> = ({ post, onTogglePinned }) => {
const navigate = useNavigate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import {
Badge,
Input,
} from '@mantine/core'
import TeamRadarMap from '@Components/TeamRadarMap'
import { BonusLabel } from '@Utils/Shared'
import { useTableStyles } from '@Utils/ThemeOverride'
import { ChallengeInfo, ScoreboardItem, SubmissionType } from '@Api'
import TeamRadarMap from './TeamRadarMap'

interface MobileScoreboardItemModalProps extends ModalProps {
item?: ScoreboardItem | null
Expand Down
4 changes: 2 additions & 2 deletions src/GZCTF/ClientApp/src/components/MobileScoreboardTable.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { FC, useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { Paper, Table, Group, Avatar, Box, Stack, Pagination, Select, Input } from '@mantine/core'
import MobileScoreboardItemModal from '@Components/MobileScoreboardItemModal'
import { ScoreboardProps, useScoreboardStyles } from '@Components/ScoreboardTable'
import { BloodBonus } from '@Utils/Shared'
import { useGameScoreboard } from '@Utils/useGame'
import { ScoreboardItem, SubmissionType } from '@Api'
import MobileScoreboardItemModal from './MobileScoreboardItemModal'
import { ScoreboardProps, useScoreboardStyles } from './ScoreboardTable'

const TableRow: FC<{
item: ScoreboardItem
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/PasswordChangeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { useInputState } from '@mantine/hooks'
import { showNotification } from '@mantine/notifications'
import { mdiCheck, mdiClose } from '@mdi/js'
import { Icon } from '@mdi/react'
import StrengthPasswordInput from '@Components/StrengthPasswordInput'
import { showErrorNotification } from '@Utils/ApiErrorHandler'
import api from '@Api'
import StrengthPasswordInput from './StrengthPasswordInput'

const PasswordChangeModal: FC<ModalProps> = (props) => {
const [oldPwd, setOldPwd] = useInputState('')
Expand Down
4 changes: 2 additions & 2 deletions src/GZCTF/ClientApp/src/components/PostCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import {
} from '@mantine/core'
import { mdiPencilOutline, mdiPinOffOutline, mdiPinOutline } from '@mdi/js'
import { Icon } from '@mdi/react'
import MarkdownRender from '@Components/MarkdownRender'
import { RequireRole } from '@Components/WithRole'
import { useUserRole } from '@Utils/useUser'
import { PostInfoModel, Role } from '@Api'
import MarkdownRender from './MarkdownRender'
import { RequireRole } from './WithRole'

export interface PostCardProps {
post: PostInfoModel
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/RecentGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {
} from '@mantine/core'
import { mdiFlagOutline } from '@mdi/js'
import { Icon } from '@mdi/react'
import { getGameStatus, GameColorMap, GameStatus } from '@Components/GameCard'
import { BasicGameInfoModel } from '@Api'
import { getGameStatus, GameColorMap, GameStatus } from './GameCard'

export interface RecentGameProps {
game: BasicGameInfoModel
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/RecentGameCarousel.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Autoplay from 'embla-carousel-autoplay'
import { FC, useRef } from 'react'
import { Carousel, CarouselProps } from '@mantine/carousel'
import RecentGameSlide from '@Components/RecentGameSlide'
import { BasicGameInfoModel } from '@Api'
import RecentGameSlide from './RecentGameSlide'

interface RecentGameCarouselProps extends CarouselProps {
games: BasicGameInfoModel[]
Expand Down
4 changes: 2 additions & 2 deletions src/GZCTF/ClientApp/src/components/RecentGameSlide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import dayjs from 'dayjs'
import { FC } from 'react'
import { Link } from 'react-router-dom'
import { Badge, Group, Stack, Title, createStyles, Paper } from '@mantine/core'
import { getGameStatus, GameColorMap, GameStatus } from './GameCard'
import { RecentGameProps } from './RecentGame'
import { getGameStatus, GameColorMap, GameStatus } from '@Components/GameCard'
import { RecentGameProps } from '@Components/RecentGame'

const useStyles = createStyles((theme) => ({
card: {
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/ScoreboardItemModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import {
Title,
Badge,
} from '@mantine/core'
import TeamRadarMap from '@Components/TeamRadarMap'
import { BloodsTypes, BonusLabel } from '@Utils/Shared'
import { useTableStyles } from '@Utils/ThemeOverride'
import { ChallengeInfo, ScoreboardItem, SubmissionType } from '@Api'
import TeamRadarMap from './TeamRadarMap'

interface ScoreboardItemModalProps extends ModalProps {
item?: ScoreboardItem | null
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/ScoreboardTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import {
Input,
} from '@mantine/core'
import { Icon } from '@mdi/react'
import ScoreboardItemModal from '@Components/ScoreboardItemModal'
import { BloodBonus, BloodsTypes, ChallengeTagLabelMap, SubmissionTypeIconMap } from '@Utils/Shared'
import { useTooltipStyles } from '@Utils/ThemeOverride'
import { useGameScoreboard } from '@Utils/useGame'
import { ChallengeInfo, ChallengeTag, ScoreboardItem, SubmissionType } from '@Api'
import ScoreboardItemModal from './ScoreboardItemModal'

export const useScoreboardStyles = createStyles((theme) => ({
table: {
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/StickyHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FC } from 'react'
import { Group, Title, Text, createStyles, keyframes } from '@mantine/core'
import LogoHeader from '@Components/LogoHeader'
import { useConfig } from '@Utils/useConfig'
import LogoHeader from './LogoHeader'

const useStyles = createStyles((theme) => ({
group: {
Expand Down
6 changes: 3 additions & 3 deletions src/GZCTF/ClientApp/src/components/WithGameMonitor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { Button, Group, LoadingOverlay, Stack, Tabs, useMantineTheme } from '@mantine/core'
import { mdiFileTableOutline, mdiFlag, mdiLightningBolt, mdiExclamationThick } from '@mdi/js'
import { Icon } from '@mdi/react'
import WithGameTab from '@Components/WithGameTab'
import WithNavBar from '@Components/WithNavbar'
import WithRole from '@Components/WithRole'
import { Role } from '@Api'
import WithGameTab from './WithGameTab'
import WithNavBar from './WithNavbar'
import WithRole from './WithRole'

const pages = [
{ icon: mdiLightningBolt, title: '事件监控', path: 'events' },
Expand Down
6 changes: 3 additions & 3 deletions src/GZCTF/ClientApp/src/components/WithGameTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { Card, Stack, Title, Text, LoadingOverlay, useMantineTheme } from '@mant
import { showNotification } from '@mantine/notifications'
import { mdiFlagOutline, mdiMonitorEye, mdiChartLine, mdiExclamationThick } from '@mdi/js'
import { Icon } from '@mdi/react'
import CustomProgress from '@Components/CustomProgress'
import IconTabs from '@Components/IconTabs'
import { RequireRole } from '@Components/WithRole'
import { useGame } from '@Utils/useGame'
import { usePageTitle } from '@Utils/usePageTitle'
import { useUserRole } from '@Utils/useUser'
import { DetailedGameInfoModel, ParticipationStatus, Role } from '@Api'
import CustomProgress from './CustomProgress'
import IconTabs from './IconTabs'
import { RequireRole } from './WithRole'

const pages = [
{
Expand Down
10 changes: 5 additions & 5 deletions src/GZCTF/ClientApp/src/components/WithNavbar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { FC } from 'react'
import { AppShell, Box, LoadingOverlay, Stack, useMantineTheme } from '@mantine/core'
import AppFooter from '@Components/AppFooter'
import AppHeader from '@Components/AppHeader'
import AppNavbar from '@Components/AppNavbar'
import Watermark from '@Components/Watermark'
import WithWiderScreen from '@Components/WithWiderScreen'
import { useUser } from '@Utils/useUser'
import AppFooter from './AppFooter'
import AppHeader from './AppHeader'
import AppNavbar from './AppNavbar'
import Watermark from './Watermark'
import WithWiderScreen from './WithWiderScreen'

interface WithNavBarProps extends React.PropsWithChildren {
width?: string
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/ClientApp/src/components/WithWiderScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FC } from 'react'
import { Stack, Title, Text } from '@mantine/core'
import { useViewportSize } from '@mantine/hooks'
import IconWiderScreenRequired from './icon/WiderScreenRequiredIcon'
import IconWiderScreenRequired from '@Components/icon/WiderScreenRequiredIcon'

interface WithWiderScreenProps extends React.PropsWithChildren {
minWidth?: number
Expand Down
Loading

0 comments on commit 9f7545e

Please sign in to comment.