// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React from 'react' import {FormattedMessage, IntlShape, injectIntl} from 'react-intl' import {BlockIcons} from '../blockIcons' import {MutableCommentBlock} from '../blocks/commentBlock' import {MutableTextBlock} from '../blocks/textBlock' import {BoardTree} from '../viewModel/boardTree' import {CardTree, MutableCardTree} from '../viewModel/cardTree' import mutator from '../mutator' import {OctoListener} from '../octoListener' import {OctoUtils} from '../octoUtils' import {PropertyMenu} from '../propertyMenu' import {Utils} from '../utils' import MenuWrapper from '../widgets/menuWrapper' import Menu from '../widgets/menu' import Button from './button' import {Editable} from './editable' import {MarkdownEditor} from './markdownEditor' import ContentBlock from './contentBlock' import CommentsList from './commentsList' import './cardDetail.scss' type Props = { boardTree: BoardTree cardId: string intl: IntlShape } type State = { cardTree?: CardTree } class CardDetail extends React.Component { private titleRef = React.createRef() private cardListener?: OctoListener shouldComponentUpdate() { return true } constructor(props: Props) { super(props) this.state = {} } componentDidMount() { this.cardListener = new OctoListener() this.cardListener.open([this.props.cardId], async (blockId) => { Utils.log(`cardListener.onChanged: ${blockId}`) await cardTree.sync() this.setState({...this.state, cardTree}) }) const cardTree = new MutableCardTree(this.props.cardId) cardTree.sync().then(() => { this.setState({...this.state, cardTree}) setTimeout(() => { if (this.titleRef.current) { this.titleRef.current.focus() } }, 0) }) } componentWillUnmount() { this.cardListener?.close() this.cardListener = undefined } render() { const {boardTree, intl} = this.props const {cardTree} = this.state const {board} = boardTree if (!cardTree) { return null } const {card, comments} = cardTree const newCommentRef = React.createRef() const sendCommentButtonRef = React.createRef() let contentElements if (cardTree.contents.length > 0) { contentElements = (
{cardTree.contents.map((block) => ( ))}
) } else { contentElements = (
{ const block = new MutableTextBlock() block.parentId = card.id block.title = text block.order = cardTree.contents.length * 1000 mutator.insertBlock(block, 'add card text') }} />
) } const icon = card.icon return ( <>
{icon &&
{icon}
mutator.changeIcon(card, BlockIcons.shared.randomIcon())} /> mutator.changeIcon(card, undefined, 'remove icon')} />
} {!icon &&
} { mutator.changeTitle(card, text) }} /> {/* Property list */}
{board.cardProperties.map((propertyTemplate) => { return (
{ const menu = PropertyMenu.shared menu.property = propertyTemplate menu.onNameChanged = (propertyName) => { Utils.log('menu.onNameChanged') mutator.renameProperty(board, propertyTemplate.id, propertyName) } menu.onMenuClicked = async (command) => { switch (command) { case 'type-text': await mutator.changePropertyType(board, propertyTemplate, 'text') break case 'type-number': await mutator.changePropertyType(board, propertyTemplate, 'number') break case 'type-createdTime': await mutator.changePropertyType(board, propertyTemplate, 'createdTime') break case 'type-updatedTime': await mutator.changePropertyType(board, propertyTemplate, 'updatedTime') break case 'type-select': await mutator.changePropertyType(board, propertyTemplate, 'select') break case 'delete': await mutator.deleteProperty(boardTree, propertyTemplate.id) break default: Utils.assertFailure(`Unhandled menu id: ${command}`) } } menu.showAtElement(e.target as HTMLElement) }} >{propertyTemplate.name}
{OctoUtils.propertyValueEditableElement(card, propertyTemplate)}
) })}
{ // TODO: Show UI await mutator.insertPropertyTemplate(boardTree) }} >
{/* Comments */}

{/* Content blocks */}
{contentElements}
{ const block = new MutableTextBlock() block.parentId = card.id block.order = cardTree.contents.length * 1000 mutator.insertBlock(block, 'add text') }} /> Utils.selectLocalFile( (file) => mutator.createImageBlock(card.id, file, cardTree.contents.length * 1000), '.jpg,.jpeg,.png', )} />
) } close() { PropertyMenu.shared.hide() } } export default injectIntl(CardDetail)