main

mattermost/focalboard

Last updated at: 29/12/2023 09:51

cardBadges.tsx

TLDR

This file, cardBadges.tsx, is a React component that renders badges for a card. The badges indicate if the card has a description, comments, or checkboxes. The component receives a card object as a prop and calculates the badges based on the card's contents and comments. If there are no badges to display, the component returns null. The file also imports and uses various other components, hooks, and utilities.

Methods

There are no methods defined in this file.

Classes

There are no classes defined in this file.

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useMemo} from 'react'
import {useIntl} from 'react-intl'

import {Card} from '../blocks/card'
import {useAppSelector} from '../store/hooks'
import {getCardContents} from '../store/contents'
import {getCardComments} from '../store/comments'
import {ContentBlock} from '../blocks/contentBlock'
import {CommentBlock} from '../blocks/commentBlock'
import TextIcon from '../widgets/icons/text'
import MessageIcon from '../widgets/icons/message'
import CheckIcon from '../widgets/icons/check'
import {Utils} from '../utils'

import './cardBadges.scss'

type Props = {
    card: Card
    className?: string
}

type Checkboxes = {
    total: number
    checked: number
}

type Badges = {
    description: boolean
    comments: number
    checkboxes: Checkboxes
}

const hasBadges = (badges: Badges): boolean => {
    return badges.description || badges.comments > 0 || badges.checkboxes.total > 0
}

type ContentsType = Array<ContentBlock | ContentBlock[]>

const calculateBadges = (contents: ContentsType, comments: CommentBlock[]): Badges => {
    let text = 0
    let total = 0
    let checked = 0

    const updateCounters = (block: ContentBlock) => {
        if (block.type === 'text') {
            text++
            const checkboxes = Utils.countCheckboxesInMarkdown(block.title)
            total += checkboxes.total
            checked += checkboxes.checked
        } else if (block.type === 'checkbox') {
            total++
            if (block.fields.value) {
                checked++
            }
        }
    }

    for (const content of contents) {
        if (Array.isArray(content)) {
            content.forEach(updateCounters)
        } else {
            updateCounters(content)
        }
    }
    return {
        description: text > 0,
        comments: comments.length,
        checkboxes: {
            total,
            checked,
        },
    }
}

const CardBadges = (props: Props) => {
    const {card, className} = props
    const contents = useAppSelector(getCardContents(card.id))
    const comments = useAppSelector(getCardComments(card.id))
    const badges = useMemo(() => calculateBadges(contents, comments), [contents, comments])
    if (!hasBadges(badges)) {
        return null
    }
    const intl = useIntl()
    const {checkboxes} = badges
    return (
        <div className={`CardBadges ${className || ''}`}>
            {badges.description &&
                <span title={intl.formatMessage({id: 'CardBadges.title-description', defaultMessage: 'This card has a description'})}>
                    <TextIcon/>
                </span>}
            {badges.comments > 0 &&
                <span title={intl.formatMessage({id: 'CardBadges.title-comments', defaultMessage: 'Comments'})}>
                    <MessageIcon/>
                    {badges.comments}
                </span>}
            {checkboxes.total > 0 &&
                <span title={intl.formatMessage({id: 'CardBadges.title-checkboxes', defaultMessage: 'Checkboxes'})}>
                    <CheckIcon/>
                    {`${checkboxes.checked}/${checkboxes.total}`}
                </span>}
        </div>
    )
}

export default React.memo(CardBadges)