// 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 {MutableTextBlock} from '../blocks/textBlock' import {BoardTree} from '../viewModel/boardTree' import {PropertyType} from '../blocks/board' import {CardTree, MutableCardTree} from '../viewModel/cardTree' import mutator from '../mutator' import {OctoListener} from '../octoListener' import {Utils} from '../utils' import MenuWrapper from '../widgets/menuWrapper' import Menu from '../widgets/menu' import PropertyMenu from '../widgets/propertyMenu' import Editable from '../widgets/editable' import Button from '../widgets/buttons/button' import EmojiIcon from '../widgets/icons/emoji' import {MarkdownEditor} from './markdownEditor' import ContentBlock from './contentBlock' import CommentsList from './commentsList' import BlockIconSelector from './blockIconSelector' import PropertyValueElement from './propertyValueElement' import './cardDetail.scss' type Props = { boardTree: BoardTree cardId: string intl: IntlShape } type State = { cardTree?: CardTree title: string } class CardDetail extends React.Component { private titleRef = React.createRef() private cardListener?: OctoListener shouldComponentUpdate() { return true } constructor(props: Props) { super(props) this.state = { title: '', } } componentDidMount() { this.createCardTreeAndSync() } private async createCardTreeAndSync() { const cardTree = new MutableCardTree(this.props.cardId) await cardTree.sync() this.createListener() this.setState({cardTree, title: cardTree.card.title}) setTimeout(() => { if (this.titleRef.current) { this.titleRef.current.focus() } }, 0) } private createListener() { this.cardListener = new OctoListener() this.cardListener.open( [this.props.cardId], async (blocks) => { Utils.log(`cardListener.onChanged: ${blocks.length}`) const newCardTree = this.state.cardTree.mutableCopy() if (newCardTree.incrementalUpdate(blocks)) { this.setState({cardTree: newCardTree, title: newCardTree.card.title}) } }, async () => { Utils.log('cardListener.onReconnect') const newCardTree = this.state.cardTree.mutableCopy() await newCardTree.sync() this.setState({cardTree: newCardTree, title: newCardTree.card.title}) }, ) } 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 let contentElements if (cardTree.contents.length > 0) { contentElements = (
{cardTree.contents.map((block) => ( ))}
) } else { contentElements = (
{ if (text) { const block = new MutableTextBlock() block.parentId = card.id block.title = text block.order = (this.state.cardTree.contents.length + 1) * 1000 mutator.insertBlock(block, 'add card text') } }} />
) } const icon = card.icon return ( <>
{!icon &&
} this.setState({title})} onSave={() => { if (this.state.title !== this.state.cardTree.card.title) { mutator.changeTitle(card, this.state.title) } }} onCancel={() => this.setState({title: this.state.cardTree.card.title})} /> {/* Property list */}
{board.cardProperties.map((propertyTemplate) => { return (
mutator.renameProperty(board, propertyTemplate.id, newName)} onTypeChanged={(newType: PropertyType) => mutator.changePropertyType(boardTree, propertyTemplate, newType)} onDelete={(id: string) => mutator.deleteProperty(boardTree, id)} />
) })}
{/* Comments */}

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